Major Changes: - Add StreamingService with OpenAI Compatible format - Upgrade Chat component V2 with Ant Design X integration - Implement AIA module with 12 intelligent agents - Update API routes to unified /api/v1 prefix - Update system documentation Backend (~1300 lines): - common/streaming: OpenAI Compatible adapter - modules/aia: 12 agents, conversation service, streaming integration - Update route versions (RVW, PKB to v1) Frontend (~3500 lines): - modules/aia: AgentHub + ChatWorkspace (100% prototype restoration) - shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook - Update API endpoints to v1 Documentation: - AIA module status guide - Universal capabilities catalog - System overview updates - All module documentation sync Tested: Stream response verified, authentication working Status: AIA V2.0 core completed (85%)
12 KiB
ASL模å<EFBFBD>— - Week 2 Day 2 完æˆ<C3A6>报告
日期: 2025-11-19
任务: 文献导入é¡?+ Excel模æ<C2A1>¿ä¸‹è½½
**状æ€?*: âœ?100% 完æˆ<C3A6>
📋 ä»Šæ—¥ç›®æ ‡å›žé¡¾
æ ¹æ<EFBFBD>®Week 2å¼€å<E282AC>‘计划,Day 2的主è¦<C3A8>ç›®æ ‡æ˜¯ï¼?
- âœ?Excel模æ<C2A1>¿ç”Ÿæˆ<C3A6>与下è½?
- âœ?Excelä¸Šä¼ ä¸Žå†…å˜è§£æž?
- âœ?å—æ®µéªŒè¯<C3A8>与去é‡?
- âœ?æ–‡çŒ®é¢„è§ˆè¡¨æ ¼
- âœ?完整æ<C2B4><C3A6>交æµ<C3A6>程(创建项ç›?导入文献ï¼?
🎯 æ ¸å¿ƒæˆ<C3A6>æžœ
1. Excel工具函数模å<C2A1>— â<C3A2>â<C3A2>â<C3A2>â<C3A2>â?
文件: frontend-v2/src/modules/asl/utils/excelUtils.ts
包å<EFBFBD>«åŠŸèƒ½:
- �
downloadExcelTemplate()- 生æˆ<C3A6>并下载Excel模æ<C2A1>¿ - âœ?
parseExcelFile()- 内å˜è§£æž<C3A6>Excel文件 - âœ?
validateLiterature()- 验è¯<C3A8>å<EFBFBD>•æ<E280A2>¡æ–‡çŒ® - âœ?
validateLiteratures()- 批é‡<C3A9>验è¯<C3A8>文献 - âœ?
deduplicateLiteratures()- 去é‡<C3A9>逻辑(DOI + Titleï¼? - âœ?
processExcelFile()- 完整处ç<E2809E>†æµ<C3A6>程
**技术亮�*:
// 内å˜è§£æž<C3A6>,ä¸<C3A4>è<EFBFBD>½ç›˜ï¼ˆäº‘原生è¦<C3A8>求ï¼?
const buffer = await data.toBuffer();
const workbook = XLSX.read(buffer, { type: 'array' });
// 智能去é‡<C3A9>(优先DOI,其次Titleï¼?
if (lit.doi && lit.doi.trim() !== '') {
key = `doi:${lit.doi.toLowerCase().trim()}`;
} else {
key = `title:${lit.title.toLowerCase().replace(/\s+/g, '')}`;
}
2. 完善çš?设置与å<C5BD>¯åŠ?页é<C2B5>¢ â<C3A2>â<C3A2>â<C3A2>â<C3A2>â?
文件: frontend-v2/src/modules/asl/pages/TitleScreeningSettings.tsx
完整功能æµ<EFBFBD>程:
用户填写PICOSæ ‡å‡†
�
ä¸Šä¼ Excel文件
�
å‰<C3A5>端内å˜è§£æž<C3A6> + 验è¯<C3A8> + 去é‡<C3A9>
�
æ˜¾ç¤ºé¢„è§ˆè¡¨æ ¼ + 统计信æ<C2A1>¯
�
点击"开始AIåˆ<C3A5>ç›"
�
创建项目 â†?导入文献 â†?跳转工作å<C593>?
UI组件:
- âœ?Excel Draggerä¸Šä¼ ç»„ä»¶
- âœ?Excel模æ<C2A1>¿ä¸‹è½½æŒ‰é’®
- âœ?è§£æž<C3A6>统计信æ<C2A1>¯å<C2AF>¡ç‰‡ï¼?个Statistic组件ï¼?
- âœ?æ–‡çŒ®é¢„è§ˆè¡¨æ ¼ï¼ˆåˆ†é¡µã€<C3A3>Tooltipã€<C3A3>çœ<C3A7>略)
- âœ?错误信æ<C2A1>¯Alert
- âœ?å<>¯åŠ¨æŒ‰é’®ï¼ˆå¸¦loading状æ€<C3A6>)
**状æ€<C3A6>管ç<C2A1>?*:
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [literatures, setLiteratures] = useState<LiteratureData[]>([]);
const [parseStats, setParseStats] = useState<ParseStatistics | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [canStart, setCanStart] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
3. API接å<C2A5>£ä¼˜åŒ– â<C3A2>â<C3A2>â?
文件: frontend-v2/src/modules/asl/api/index.ts
修改内容:
- �修改
importLiteraturesç¾å<EFBFBD><EFBFBD>,接å<EFBFBD>—{projectId, literatures}对象 - âœ?导出统一的
aslApi对象,便于组件引�
使用示例:
import { aslApi } from '../api';
// 创建项目
const response = await aslApi.createProject({...});
// 导入文献
await aslApi.importLiteratures({
projectId,
literatures: [...],
});
🔧 技术实现细�
1. Excel模æ<C2A1>¿ç”Ÿæˆ<C3A6>
**包å<E280A6>«ä¸¤ä¸ªå·¥ä½œè¡?*:
-
文献列表:
- 3行示例数æ<EFBFBD>?
- 7列(Title, Abstract, PMID, Authors, Journal, Year, DOI�
- 自动设置列宽
-
å—æ®µè¯´æ˜Ž:
- æ¯<EFBFBD>ä¸ªå—æ®µçš„说æ˜?
- å¿…å¡«/å<>¯é€‰æ ‡è®?
- 使用建议
代ç <EFBFBD>片段:
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(templateData);
ws['!cols'] = [
{ wch: 60 }, // Title
{ wch: 80 }, // Abstract
// ...
];
XLSX.utils.book_append_sheet(wb, ws, '文献列表');
XLSX.writeFile(wb, '文献导入模æ<C2A1>¿.xlsx');
2. 内å˜è§£æž<C3A6>(云原生ï¼?
**é<>µå¾ªäº‘原生开å<E282AC>‘è§„èŒ?*:
- �使用
FileReader.readAsArrayBuffer()读å<EFBFBD>–文件 - âœ?使用
XLSX.read(buffer, { type: 'array' })内å˜è§£æž<EFBFBD> - âœ?**ä¸<C3A4>è<EFBFBD>½ç›?*,直接在内å˜ä¸å¤„ç<E2809E>?
**å—æ®µæ˜ 射(支æŒ<C3A6>ä¸è‹±æ–‡ï¼?*:
title: row.Title || row.title || row['æ ‡é¢˜'] || ''
abstract: row.Abstract || row.abstract || row['摘è¦<C3A8>'] || ''
pmid: row.PMID || row.pmid || row['PMIDç¼–å<E28093>·'] || ''
// ...
3. æ•°æ<C2B0>®éªŒè¯<C3A8>
验è¯<EFBFBD>规则:
title: 必填,至å°?0个å—ç¬?abstract: 必填,至å°?0个å—ç¬?- å…¶ä»–å—æ®µ: å<>¯é€?
错误æ<EFBFBD><EFBFBD>示:
if (!lit.title || lit.title.length < 10) {
errors.push('æ ‡é¢˜å¤ªçŸï¼ˆè‡³å°?0个å—符)');
}
4. 智能去é‡<C3A9>
去é‡<EFBFBD>ç–ç•¥:
- 优先çº?: DOI(如果å˜åœ¨ï¼‰
- æ ‡å‡†åŒ–ï¼šè½¬å°<EFBFBD>写ã€<EFBFBD>åŽ»ç©ºæ ¼
- 优先�: Title
- æ ‡å‡†åŒ–ï¼šè½¬å°<EFBFBD>写ã€<EFBFBD>去除所有空白å—ç¬?
代ç <EFBFBD>逻辑:
const seen = new Map<string, LiteratureData>();
for (const lit of literatures) {
let key: string;
if (lit.doi && lit.doi.trim() !== '') {
key = `doi:${lit.doi.toLowerCase().trim()}`;
} else {
key = `title:${lit.title.toLowerCase().replace(/\s+/g, '')}`;
}
if (!seen.has(key)) {
seen.set(key, lit);
unique.push(lit);
} else {
duplicates.push(lit);
}
}
5. æ–‡çŒ®é¢„è§ˆè¡¨æ ¼
**功能特�*:
- âœ?分页显示ï¼?0æ<30>?页)
- âœ?æ ‡é¢˜å’Œæ‘˜è¦<C3A8>è¶…é•¿çœ<C3A7>略(Tooltip显示全文ï¼?
- âœ?å“<C3A5>应å¼<C3A5>布局(scroll xï¼?
- âœ?åº<C3A5>å<EFBFBD>·è‡ªåŠ¨è®¡ç®—
**列定�*:
const columns = [
{ title: '#', width: 60, render: (_, __, index) => index + 1 },
{ title: 'æ ‡é¢˜', width: '35%', ellipsis: true },
{ title: '摘è¦<C3A8>', width: '30%', ellipsis: true, render: truncate },
{ title: 'PMID', width: 100 },
{ title: '年份', width: 80 },
{ title: '作�, ellipsis: true },
];
6. 统计信æ<C2A1>¯å±•示
**4个统计指æ ?*:
<Row gutter={16}>
<Col span={6}>
<Statistic title="总数" value={total} prefix={<CheckCircle />} />
</Col>
<Col span={6}>
<Statistic title="有效" value={afterDedup} valueStyle={{ color: '#3f8600' }} />
</Col>
<Col span={6}>
<Statistic title="é‡<C3A9>å¤<C3A5>" value={duplicates} valueStyle={{ color: '#faad14' }} />
</Col>
<Col span={6}>
<Statistic title="æ— æ•ˆ" value={invalid} valueStyle={{ color: '#cf1322' }} />
</Col>
</Row>
📊 完整交互æµ<C3A6>程
[用户进入页é<C2B5>¢]
�
[填写PICOS表å<C2A8>•] â†?必填(P, I, C, S, 纳入, 排除ï¼?
�
[选择ç›é€‰é£Žæ ¼] â†?Radio(lenient/standard/strictï¼?
�
[点击"下载Excel模æ<C2A1>¿"] â†?å<>¯é€?
â†?下载包å<E280A6>«ç¤ºä¾‹çš„Excel模æ<C2A1>¿
�
[ä¸Šä¼ Excel文件] â†?Dragger组件
�
[å‰<C3A5>端自动解æž<C3A6>] â†?xlsx.read()
�
├─ å—æ®µæ˜ 射(ä¸è‹±æ–‡å…¼å®¹ï¼?
├─ æ•°æ<C2B0>®éªŒè¯<C3A8>(title + abstractå¿…å¡«ï¼?
└─ 去é‡<C3A9>逻辑(DOI â†?Titleï¼?
�
[显示统计信æ<C2A1>¯] â†?4个Statisticå<63>¡ç‰‡
├─ 总数: 100�
├─ 有效: 95篇(绿色�
├─ é‡<C3A9>å¤<C3A5>: 3篇(橙色ï¼?
└─ æ— æ•ˆ: 2篇(红色ï¼?
�
[æ˜¾ç¤ºæ–‡çŒ®é¢„è§ˆè¡¨æ ¼] â†?Table + Pagination
�
[ç”¨æˆ·ç¡®è®¤æ— è¯¯]
�
[点击"开始AIåˆ<C3A5>ç›"] â†?Button(disabled如果未完æˆ<C3A6>)
�
[Loading: "æ£åœ¨åˆ›å»ºé¡¹ç›®..."]
�
[API: createProject()] �获得projectId
�
[Loading: "æ£åœ¨å¯¼å…¥æ–‡çŒ®..."]
�
[API: importLiteratures({ projectId, literatures })]
�
[Success: "项目创建æˆ<C3A6>功ï¼?]
�
[自动跳转] �/literature/screening/title/workbench
🎨 UI/UX亮点
1. å<>‹å¥½çš„错误æ<C2AF><C3A6>ç¤?
分层æ<EFBFBD><EFBFBD>示:
- âœ?文件解æž<C3A6>错误 â†?红色Message
- âœ?æ•°æ<C2B0>®éªŒè¯<C3A8>失败 â†?橙色Alert(显示å‰<C3A5>5æ<35>¡é”™è¯¯ï¼‰
- âœ?é‡<C3A9>å¤<C3A5>æ•°æ<C2B0>® â†?è“<C3A8>色Info Message
- âœ?æ— æ•ˆæ•°æ<C2B0>® â†?橙色Warning Message
示例:
if (statistics.invalid > 0) {
message.warning(`æœ?${statistics.invalid} æ<>¡æ•°æ<C2B0>®éªŒè¯<C3A8>失败,已自动过滤`, 3);
}
if (statistics.duplicates > 0) {
message.info(`检测到 ${statistics.duplicates} æ<>¡é‡<C3A9>å¤<C3A5>æ•°æ<C2B0>®ï¼Œå·²è‡ªåŠ¨åŽ»é‡<C3A9>`, 3);
}
2. 优雅的Loading状�
三ç§<EFBFBD>Loading:
- **è§£æž<C3A6>ä¸?*:
message.loading('æ£åœ¨è§£æž<C3A6>Excel文件...') - 创建项目:
message.loading('æ£åœ¨åˆ›å»ºé¡¹ç›®...') - 导入文献:
message.loading('æ£åœ¨å¯¼å…¥æ–‡çŒ®...')
**Button状�*:
<Button
type="primary"
size="large"
loading={isSubmitting}
disabled={!canStart}
>
{isSubmitting ? 'æ£åœ¨åˆ›å»ºé¡¹ç›®å¹¶å¯¼å…¥æ–‡çŒ?..' : '开始AIæ ‡é¢˜æ‘˜è¦<C3A8>åˆ<C3A5>ç›'}
</Button>
3. æ¸<C3A6>è¿›å¼<C3A5>å<EFBFBD>¯ç”?
**å<>¯åŠ¨æŒ‰é’®æ¿€æ´»æ<C2BB>¡ä»?*:
setCanStart(formValid && valid.length > 0);
- â<EFBFBD>?表å<C2A8>•未填å†?â†?按钮ç¦<C3A7>用
- â<EFBFBD>?文献未导å…?â†?按钮ç¦<C3A7>用
- âœ?表å<C2A8>•+文献都完æˆ?â†?按钮å<C2AE>¯ç”¨
æ<EFBFBD><EFBFBD>示信æ<EFBFBD>¯:
{!canStart && literatures.length === 0 && (
<Alert
message="请先完æˆ<C3A6>以上æ¥éª¤"
description="填写PICOSæ ‡å‡†ã€<C3A3>纳å…?æŽ’é™¤æ ‡å‡†ï¼Œå¹¶å¯¼å…¥æ–‡çŒ®å<C2AE>Žï¼Œå<C592>³å<C2B3>¯å¼€å§‹ç›é€?
type="warning"
/>
)}
📦 新增/修改的文�
新增文件 �
frontend-v2/src/modules/asl/utils/excelUtils.ts- 366行代ç ?
- 完整的Excel处ç<EFBFBD>†å·¥å…·åº?
修改文件 �
-
frontend-v2/src/modules/asl/pages/TitleScreeningSettings.tsx- 从å<EFBFBD> ä½<EFBFBD>页é<EFBFBD>?â†?完整功能页é<C2B5>¢
- 新增597行代ç ?
-
frontend-v2/src/modules/asl/api/index.ts- 修改
importLiteratures函数ç¾å<EFBFBD><EFBFBD> - 新增
aslApi统一导出对象
- 修改
ä¾<EFBFBD>赖安装 âœ?
npm install xlsx
# 新增ä¾<C3A4>赖:xlsx@^0.18.5
âœ?完æˆ<C3A6>æ ‡å‡†éªŒè¯<C3A8>
功能完整��
- Excel模æ<EFBFBD>¿ä¸‹è½½åŠŸèƒ½
- Excelä¸Šä¼ ä¸Žè§£æž<EFBFBD>(内å˜ï¼Œä¸<EFBFBD>è<EFBFBD>½ç›˜ï¼?
- å—æ®µéªŒè¯<EFBFBD>(title + abstractå¿…å¡«ï¼?
- 去é‡<EFBFBD>逻辑(DOI + Titleï¼?
- æ–‡çŒ®é¢„è§ˆè¡¨æ ¼
- 统计信æ<EFBFBD>¯å±•示
- "开始AIåˆ<C3A5>ç›"按钮å<C2AE>¯ç”¨
- 一次性创建项�导入文献
代ç <EFBFBD>è´¨é‡<EFBFBD> âœ?
- æ— TypeScript类型错误
- æ— ESLintè¦å‘Š
- 符å<EFBFBD>ˆReact最佳实è·?
- 完整的错误处ç<EFBFBD>?
- å<EFBFBD>‹å¥½çš„用户æ<EFBFBD><EFBFBD>ç¤?
云原生è¦<EFBFBD>æ±?âœ?
- 内å˜è§£æž<EFBFBD>Excel(ä¸<EFBFBD>è<EFBFBD>½ç›˜ï¼?
- 使用环境å<EFBFBD>˜é‡<EFBFBD>é…<EFBFBD>ç½®
- æ— å<EFBFBD>Œæ¥é˜»å¡žæ“<EFBFBD>ä½?
- 完整的异æ¥å¤„ç<EFBFBD>?
🧪 测试建议
测试场景
-
æ£å¸¸æµ<EFBFBD>程:
- 下载模æ<EFBFBD>¿ â†?填写数æ<C2B0>® â†?ä¸Šä¼ â†?预览 â†?å<>¯åЍ
-
异常场景:
- ä¸Šä¼ ç©ºæ–‡ä»?
- ä¸Šä¼ é<EFBFBD>žExcel文件
- å¿…å¡«å—æ®µç¼ºå¤±
- æ•°æ<EFBFBD>®æ ¼å¼<EFBFBD>错误
- é‡<EFBFBD>å¤<EFBFBD>æ•°æ<EFBFBD>®å¤„ç<EFBFBD>†
-
边界场景:
- 1篇文�
- 500+篇文�
- 䏿–‡åˆ—å<EFBFBD><EFBFBD>
- 英文列å<EFBFBD><EFBFBD>
- æ··å<EFBFBD>ˆåˆ—å<EFBFBD><EFBFBD>
ðŸ“<EFBFBD> 使用说明
1. 下载模æ<C2A1>¿
点击"下载Excel模æ<C2A1>¿"按钮
â†?自动下载"文献导入模æ<C2A1>¿.xlsx"
â†?包å<E280A6>«2个工作表ï¼?
├─ 文献列表(带示例�
└─ å—æ®µè¯´æ˜Ž
2. 填写数æ<C2B0>®
å¿…å¡«å—æ®µ:
Title: æ–‡çŒ®æ ‡é¢˜ï¼ˆè‡³å°?0å—符ï¼?Abstract: 文献摘è¦<C3A8>(至å°?0å—符ï¼?
**å<>¯é€‰å—æ®?*:
PMID: PubMed IDAuthors: 作者列è¡?Journal: 期刊å<C5A0><C3A5>ç§°Year: å<>‘表年份DOI: DOIç¼–å<E28093>·ï¼ˆç”¨äºŽåŽ»é‡<C3A9>)
3. ä¸Šä¼ è§£æž<C3A6>
点击或拖拽Excelæ–‡ä»¶åˆ°ä¸Šä¼ åŒºåŸ?
â†?自动解æž<C3A6>ï¼?-5秒)
â†?显示统计信æ<C2A1>¯å’Œé¢„览表æ ?
â†?如有错误,显示详细æ<E280A0><C3A6>ç¤?
4. å<>¯åЍç›é€?
确认数æ<EFBFBD>®æ— 误å<EFBFBD>?
â†?点击"开始AIæ ‡é¢˜æ‘˜è¦<C3A8>åˆ<C3A5>ç›"
�自动创建项目
�导入所有文�
â†?è·³è½¬åˆ°å®¡æ ¸å·¥ä½œå<C593>°
🚀 下一æ¥è®¡åˆ?
Week 2 Day 3-4(明天开始)
主è¦<EFBFBD>任务: å®¡æ ¸å·¥ä½œå<C593>°ï¼ˆå<CB86>Œè¡Œè¡¨æ ¼ï¼?
预计实现:
- å<EFBFBD>Œè¡Œè¡¨æ ¼UI(主è¡?+ 展开行)
- 两个模型的判æ–结果对æ¯?
- 冲çª<EFBFBD>高亮显示
- PICO判æ–详情
- å<EFBFBD>Œè§†å›¾åŽŸæ–‡å®¡æŸ¥Modal
- 人工å¤<EFBFBD>æ ¸åŠŸèƒ½
**å<>‚考原åž?*: docs/03-业务模å<C2A1>—/ASL-AI智能文献/03-UI设计/AI智能文献-æ ‡é¢˜æ‘˜è¦<C3A8>åˆ<C3A5>ç›åŽŸåž‹.html
💡 ç»<C3A7>验总结
技术亮ç‚?â?
- **内å˜è§£æž<C3A6>**:完全符å<C2A6>ˆäº‘原生è¦<C3A8>æ±‚ï¼Œæ— æ–‡ä»¶è<C2B6>½ç›˜
- **智能去é‡<C3A9>**:DOI优先çº?+ Title兜底,实用性强
- **æ¸<C3A6>è¿›å¼<C3A5>交äº?*:表å<C2A8>•验è¯?+ 文献导入 â†?å<>¯ç”¨æŒ‰é’®
- **完整错误处ç<E2809E>†**:分层æ<E2809A><C3A6>示,用户体验å¥?
改进空间
- **大文件优åŒ?*:目å‰<C3A5>未é™<C3A9>制文件大å°<C3A5>,å<C592>¯è€ƒè™‘æ·»åŠ
- **è§£æž<C3A6>进度æ<C2A6>?*:对于超大文件,å<C592>¯æ˜¾ç¤ºè§£æž<C3A6>è¿›åº?
- **Excelæ ¼å¼<C3A5>检æŸ?*:å<C5A1>¯æ<C2AF><C3A6>å‰<C3A5>检查列å<E28094><C3A5>是å<C2AF>¦åŒ¹é…?
- 批é‡<EFBFBD>æ“<EFBFBD>ä½œï¼šè¡¨æ ¼å<EFBFBD>¯æ”¯æŒ<EFBFBD>批é‡<EFBFBD>åˆ é™¤æ— æ•ˆè¡?
📊 å¼€å<E282AC>‘统è®?
| æŒ‡æ ‡ | æ•°å€? |
|---|---|
| 新增代ç <EFBFBD> | ~1,000è¡? |
| 新增文件 | 1� |
| 修改文件 | 2� |
| 新增ä¾<EFBFBD>èµ– | 1个(xlsxï¼? |
| *功能� | 6� |
| *å¼€å<EFBFBD>‘æ—¶é—? | çº?å°<C3A5>æ—¶ |
| *完æˆ<EFBFBD>åº? | 100% âœ? |
🎉 Day 2 完æˆ<C3A6>总结
âœ?*Excel模æ<EFBFBD>¿ç”Ÿæˆ<EFBFBD>与下è½? - 完美实现
âœ?*Excelä¸Šä¼ ä¸Žè§£æž? - 内å˜å¤„ç<E2809E>†ï¼Œäº‘原生
âœ?*æ•°æ<EFBFBD>®éªŒè¯<EFBFBD>与去é‡? - 智能去é‡<C3A9>,完整验è¯?
âœ?æ–‡çŒ®é¢„è§ˆè¡¨æ ¼ - å<>‹å¥½å±•示,分页支æŒ?
âœ?完整æ<EFBFBD><EFBFBD>交æµ<EFBFBD>程 - 一键å<C2AE>¯åŠ¨ï¼Œè‡ªåŠ¨è·³è½¬
*Day 2任务 100% 完æˆ<C3A6>ï¼? 🎊
明天继ç»Week 2 Day 3-4å¼€å<E282AC>‘ï¼<C3AF> 💪
完æˆ<EFBFBD>æ—¶é—´: 2025-11-19
下一个工作日: Week 2 Day 3