Release v 1.3#211
Open
pendingintent wants to merge 40 commits into
Open
Conversation
… are assigned to the selected timeline
…e cross-site scripting' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…e cross-site scripting' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…e cross-site scripting' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… remote source' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…cript without escaping
…t,epoch.type,epoch.epoch_uid; rollback deletes entities in coirrect order; made safe for legacy db deployments
…ne matrix so the user can schedule it
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Comment on lines
+191
to
+192
| f"<div class='error' style='color:#c62828;'>" | ||
| f"Amendment creation failed: {err}</div>", |
| primary_reason_other: str = Form(""), | ||
| ): | ||
| if not soa_exists(soa_id): | ||
| raise HTTPException(404, "SOA not found") |
Comment on lines
+420
to
+424
| def _require_other_reason(self): | ||
| if self.primary_reason_code == "C17649" and not self.primary_reason_other: | ||
| raise ValueError( | ||
| "otherReason is required when primary reason code is C17649" | ||
| ) |
Comment on lines
+410
to
+444
| class StudyAmendmentCreate(BaseModel): | ||
| name: str | ||
| number: str | ||
| summary: str | ||
| label: Optional[str] = None | ||
| description: Optional[str] = None | ||
| primary_reason_code: str | ||
| primary_reason_other: Optional[str] = None | ||
|
|
||
| @model_validator(mode="after") | ||
| def _require_other_reason(self): | ||
| if self.primary_reason_code == "C17649" and not self.primary_reason_other: | ||
| raise ValueError( | ||
| "otherReason is required when primary reason code is C17649" | ||
| ) | ||
| return self | ||
|
|
||
|
|
||
| class StudyAmendmentUpdate(BaseModel): | ||
| name: Optional[str] = None | ||
| number: Optional[str] = None | ||
| summary: Optional[str] = None | ||
| label: Optional[str] = None | ||
| description: Optional[str] = None | ||
|
|
||
|
|
||
| class StudyAmendmentReasonCreate(BaseModel): | ||
| code: str | ||
| other_reason: Optional[str] = None | ||
|
|
||
| @model_validator(mode="after") | ||
| def _require_other(self): | ||
| if self.code == "C17649" and not self.other_reason: | ||
| raise ValueError("otherReason is required when reason code is C17649") | ||
| return self |
Comment on lines
+26
to
+33
| <label class="am-field-label">Primary Reason <span class="am-required">*</span></label> | ||
| <select name="primary_reason_code" required class="am-select" | ||
| onchange="var o=this.options[this.selectedIndex];document.getElementById('primary-other-row').style.display=o.getAttribute('data-code')==='C17649'?'flex':'none';"> | ||
| <option value="" disabled selected>— select reason —</option> | ||
| {% for code, sv in reason_terms.items()|sort(attribute='1') %} | ||
| <option value="{{ sv }}" data-code="{{ code }}">{{ sv }}</option> | ||
| {% endfor %} | ||
| </select> |
Comment on lines
+22
to
+31
| <select name="code" required class="am-select" | ||
| onchange="var o=this.options[this.selectedIndex];this.closest('form').querySelector('.other-reason-box').style.display=o.getAttribute('data-code')==='C17649'?'flex':'none';"> | ||
| <option value="" disabled selected>— select reason —</option> | ||
| {% for code, sv in reason_terms.items()|sort(attribute='1') %} | ||
| <option value="{{ sv }}" data-code="{{ code }}">{{ sv }}</option> | ||
| {% endfor %} | ||
| </select> | ||
| <div class="other-reason-box am-field" style="display:none"> | ||
| <input name="other_reason" class="am-input" placeholder="Specify other reason" /> | ||
| </div> |
Comment on lines
+408
to
+417
| if not code_uid: | ||
| return { | ||
| "id": f"Code_{kind}Level_unknown", | ||
| "extensionAttributes": [], | ||
| "code": "", | ||
| "codeSystem": "", | ||
| "codeSystemVersion": "", | ||
| "decode": "", | ||
| "instanceType": "Code", | ||
| } |
Comment on lines
+422
to
+463
| def get_next_biomedical_concept_property_uid(cur: Any, soa_id: int) -> str: | ||
| """Compute next unique BiomedicalConceptProperty_N for the given SOA. | ||
|
|
||
| Assumes `cur` is a sqlite cursor within an open transaction. | ||
| """ | ||
| cur.execute( | ||
| "SELECT biomedical_concept_property_uid FROM" | ||
| " biomedical_concept_property WHERE soa_id=?" | ||
| " AND biomedical_concept_property_uid LIKE" | ||
| " 'BiomedicalConceptProperty_%'", | ||
| (soa_id,), | ||
| ) | ||
| existing = [x[0] for x in cur.fetchall() if x[0]] | ||
| n = 1 | ||
| if existing: | ||
| try: | ||
| n = max(int(x.split("_")[1]) for x in existing) + 1 | ||
| except Exception: | ||
| n = len(existing) + 1 | ||
| return f"BiomedicalConceptProperty_{n}" | ||
|
|
||
|
|
||
| def get_next_extension_attribute_uid(cur: Any, soa_id: int) -> str: | ||
| """Compute next unique ExtensionAttribute_N for the given SOA. | ||
|
|
||
| Assumes `cur` is a sqlite cursor within an open transaction. | ||
| """ | ||
| cur.execute( | ||
| "SELECT extension_attribute_uid FROM activity_concept_dss" | ||
| " WHERE soa_id=?" | ||
| " AND extension_attribute_uid LIKE 'ExtensionAttribute_%'", | ||
| (soa_id,), | ||
| ) | ||
| existing = [x[0] for x in cur.fetchall() if x[0]] | ||
| n = 1 | ||
| if existing: | ||
| try: | ||
| n = max(int(x.split("_")[1]) for x in existing) + 1 | ||
| except Exception: | ||
| n = len(existing) + 1 | ||
| return f"ExtensionAttribute_{n}" | ||
|
|
Comment on lines
+58
to
+72
| echo "Retrieving storage account key..." | ||
| STORAGE_KEY=$(az storage account keys list \ | ||
| --resource-group "$RESOURCE_GROUP" \ | ||
| --account-name "$STORAGE_ACCOUNT" \ | ||
| --query '[0].value' \ | ||
| --output tsv 2>&1) | ||
|
|
||
| if [ $? -ne 0 ]; then | ||
| echo -e "${RED}Error: Failed to retrieve storage account key${NC}" | ||
| echo "Please verify:" | ||
| echo " 1. Resource group '$RESOURCE_GROUP' exists" | ||
| echo " 2. Storage account '$STORAGE_ACCOUNT' exists" | ||
| echo " 3. You have permission to access the storage account" | ||
| exit 1 | ||
| fi |
Comment on lines
+18
to
+26
| # Run database migrations (handled by app lifespan) | ||
| # Start gunicorn with uvicorn worker | ||
| gunicorn soa_builder.web.app:app \ | ||
| --bind 0.0.0.0:8000 \ | ||
| --workers 1 \ | ||
| --worker-class uvicorn.workers.UvicornWorker \ | ||
| --timeout 120 \ | ||
| --access-logfile - \ | ||
| --error-logfile - No newline at end of file |
Comment on lines
+13
to
+17
| # Configuration - UPDATE THESE VALUES | ||
| RESOURCE_GROUP="rg-soa-workbench-prod" | ||
| STORAGE_ACCOUNT="soaworkbenchsa" # Replace <uniqueid> with your storage account suffix | ||
| FILE_SHARE="soa-workbench-data" | ||
| LOCAL_DB="soa_builder_web.db" |
Comment on lines
+57
to
+72
| # Get storage account key | ||
| echo "Retrieving storage account key..." | ||
| STORAGE_KEY=$(az storage account keys list \ | ||
| --resource-group "$RESOURCE_GROUP" \ | ||
| --account-name "$STORAGE_ACCOUNT" \ | ||
| --query '[0].value' \ | ||
| --output tsv 2>&1) | ||
|
|
||
| if [ $? -ne 0 ]; then | ||
| echo -e "${RED}Error: Failed to retrieve storage account key${NC}" | ||
| echo "Please verify:" | ||
| echo " 1. Resource group '$RESOURCE_GROUP' exists" | ||
| echo " 2. Storage account '$STORAGE_ACCOUNT' exists" | ||
| echo " 3. You have permission to access the storage account" | ||
| exit 1 | ||
| fi |
Comment on lines
+420
to
+427
| cur.execute( | ||
| "SELECT codelist_table, codelist_code, code " | ||
| "FROM code_association WHERE soa_id=? AND code_uid=? LIMIT 1", | ||
| (soa_id, code_uid), | ||
| ) | ||
| row = cur.fetchone() | ||
| conn.close() | ||
| if not row: |
Comment on lines
1
to
7
| LICENSE | ||
| README.md | ||
| pyproject.toml | ||
| src/sdtm/generate_ta.py | ||
| src/sdtm/generate_te.py | ||
| src/sdtm/generate_tv.py | ||
| src/soa_builder/__init__.py |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.