Skip to content

Repository files navigation

AURA FARM

"งานกลุ่มไม่ได้ขาดข้อมูล มันขาดคนกล้าพูด" ระบบวัดแรงงานจริงในงานกลุ่มจาก revision history แล้วยิงกลับเป็นมีม Track: Applied Data Engineering · 67HACK THE MEME Engineering for Next Generation Learners

AURA FARM อ่าน ร่องรอยการทำงานที่มีชื่อผู้ทำและเวลากำกับ คำนวณสัดส่วนแรงงานจริงของสมาชิกแต่ละคน วัดความเหลื่อมล้ำด้วย Gini coefficient แล้วแปลงตัวเลขเป็นมีม PNG ที่กลุ่มดูพร้อมกันได้

เส้นแบ่งของระบบนี้ไม่ใช่ "ออนไลน์ vs ออฟไลน์" และไม่ใช่ "Google vs Microsoft" เส้นแบ่งจริงคือ เครื่องมือนั้นบันทึกไหมว่าใครแก้อะไรตอนไหน

เครื่องมือ มีบันทึกผู้แก้ไข? สถานะ
Google Docs / Slides / Sheets Drive Activity + Revisions API ✅ ใช้ได้
Word ที่เปิด Track Changes w:ins / w:del ในไฟล์เอง ✅ ใช้ได้ ลากไฟล์มาวาง
Word Online / OneDrive / SharePoint Microsoft Graph driveItem/versions ✅ ใช้ได้ ต้องมี token
GitHub commits ✅ ใช้ได้
Word ในเครื่อง ไม่เปิด Track Changes รู้แค่คนที่เซฟล่าสุด ❌ ทำไม่ได้
Canva / Figma มี version history แต่ API ไม่เปิดรายละเอียด ❌ ยังไม่รองรับ
ส่งไฟล์กันทาง LINE ไม่มีบันทึกอะไรเลย ❌ ทำไม่ได้

AURA FARM ไม่ได้ผูกกับ Google มันผูกกับ "ร่องรอยที่มีชื่อและเวลา" เครื่องมือไหนบันทึกไว้ เราอ่านได้หมด เครื่องมือไหนไม่บันทึก ไม่มีใครอ่านได้ รวมทั้งเรา

คนที่ทำพอดีส่วนของตัวเอง = 0 ออร่า · ทำเกิน = บวก · ทำน้อย = ลบ


เริ่มใช้งาน

npm install && npm run dev

เปิด http://localhost:3000 ใช้ได้ทันที ไม่ต้องตั้งค่า env ใดๆ และไม่ต้อง login ระบบทำงานใน mock mode เป็นค่าเริ่มต้น

คำสั่ง ผล
npm run dev รัน dev server
npm test รัน unit test 57 เคส
npm run build build production
npm run generate:mock สร้าง data/mock-projects.json ใหม่จาก seeded PRNG + parse ไฟล์ .docx ตัวอย่าง (ปกติไม่ต้องรัน)
npm run generate:fixtures สร้างไฟล์ .docx สำหรับเทสต์และไฟล์ตัวอย่าง (ปกติไม่ต้องรัน)

ชุดข้อมูลเดโม่

URL เรื่อง ผลที่ได้
/dashboard?demo=chaos 5 คน คนเดียวเขียน 78% อีก 2 คนแตะนิดเดียว 2 คนไม่แตะเลย Gini 0.69 → ONE_MAN_ARMY + ยิงเตือนตอน 68% ของ deadline
/dashboard?demo=dumper 4 คน ปริมาณงานใกล้เคียงกัน แต่ 2 คนพิมพ์ทั้งหมดในชั่วโมงท้าย Gini 0.10 แต่ติดธง "โยนทีเดียว" 2 คน
/dashboard?demo=legend 4 คน แบ่งงานกันจริง Gini 0.13 → LEGENDARY
/dashboard?demo=word 4 คน จากไฟล์ Word จริงที่เปิด Track Changes ไม่ผ่าน Google เลย Gini 0.49 → TILTING

ชุด word ไม่ได้เขียนมือ — มันถูก parse จากไฟล์ .docx จริง ที่ public/samples/รายงานกลุ่ม-tracked.docx ตอนรัน npm run generate:mock แล้วอบผลลง JSON ทำให้ข้อมูลเดโม่มาจาก code path เดียวกับที่ผู้ใช้จริงใช้

ลากไฟล์ .docx มาวาง

.docx คือไฟล์ zip ข้างในมี word/document.xml ถ้าเปิด Track Changes ทุกการพิมพ์จะถูกห่อด้วย <w:ins> ทุกการลบห่อด้วย <w:del> โดยมี w:author และ w:date ติดมาด้วย — นี่คือ Edit[] ครบทุกฟิลด์ อยู่ในไฟล์เอง ไม่ต้องใช้ API ไม่ต้อง OAuth ไม่ต้องต่อเน็ต

กับดักที่กันไว้แล้ว (มี test ทุกข้อ):

เรื่อง ระบบทำอะไร
ข้อความที่ถูกลบอยู่ใน w:delText ไม่ใช่ w:t นับทั้งสอง tag — test ยืนยันว่า charsRemoved ไม่เป็น 0
w:ins ซ้อนใน w:del ข้าม node ที่ซ้อน ไม่นับซ้ำ แล้วรายงานจำนวนที่ข้าม
Word ลบข้อมูลส่วนบุคคล ชื่อกลายเป็น Author ปฏิเสธไฟล์ พร้อมบอกวิธีปิดค่านั้น
w:date หาย ข้าม node นั้น แล้วรายงานจำนวนให้ผู้ใช้เห็น
ชื่อใน Word เป็นชื่อโปรไฟล์ Office ให้ผู้ใช้ map ชื่อ → สมาชิกบนหน้าจอก่อนวิเคราะห์
ไฟล์ไม่ได้เปิด Track Changes บอกวิธีเปิด (รีวิว › ติดตามการเปลี่ยนแปลง) และบอกตรงๆ ว่าเปิดย้อนหลังไม่ได้

ตัวเลขต้องนิ่ง

ระบบวัดที่วัดซ้ำแล้วได้ไม่เท่าเดิม ไม่ใช่ระบบวัด โปรเจกต์นี้จึงบังคับสามข้อ:

  1. ไม่มี Date.now() และ Math.random() ใน lib/ และ components/ เลย — มี test ที่ grep ไฟล์จริงเพื่อกันการเผลอใส่กลับเข้ามา
  2. elapsedRatio คิดจาก evaluatedAt ที่ฝังอยู่ในชุดข้อมูล ไม่ใช่นาฬิกาของเครื่อง
  3. mock data เป็นไฟล์นิ่งที่ commit ไว้ สร้างครั้งเดียวจาก scripts/generate-mock.ts ด้วย mulberry32 (seed: chaos 6767, dumper 1212, legend 4949) ตอน runtime อ่าน JSON อย่างเดียว

ผลคือรีเฟรชกี่ครั้ง เปิดวันไหน เครื่องใคร ก็ได้เลขชุดเดิมเป๊ะ


Demo script 90 วินาที

เวลา ทำอะไร พูดอะไร
0–5 วิ เปิด / — AuraCard สามใบนับเลขขึ้นลงอยู่ "มีมนี้ทุกคนรู้จัก งานกลุ่ม 5 คน ทำจริงคนเดียว"
5–20 วิ กด ดู mock project?demo=chaos ดู sequence พลิกการ์ด "เราวัดมันได้ Lorenz วาดตัวเองก่อน แล้วการ์ดพลิกทีละใบ คนแบกพลิกเป็นใบสุดท้าย"
20–40 วิ ชี้ Gini 0.63 + Lorenz curve "นี่ไม่ใช่ความรู้สึก นี่คือตัวเลข พื้นที่แรเงาคือความเหลื่อมล้ำ"
40–60 วิ สลับไป ?demo=dumper "ระบบจับคนที่ปริมาณผ่าน แต่โยนทีเดียวคืนสุดท้ายได้ ทุกคน volume 25% เท่ากัน แต่สองคนติดธง"
60–80 วิ กด สร้างมีมส่งเข้ากลุ่ม → โหลด PNG "นี่คือสิ่งที่ส่งเข้ากลุ่ม ไม่ใช่รายงาน"
80–90 วิ เปิด /methodology "และเราเปิดสูตรให้ตรวจทั้งหมด รวมถึงข้อจำกัดของตัวเอง"

อัลกอริทึม

ทั้งหมดเป็น pure function ใน lib/scoring.ts และ lib/gini.ts (ดูคำอธิบายฉบับเต็มที่หน้า /methodology)

netChars_i    = max(0, Σ charsAdded − Σ charsRemoved)
volume_i      = netChars_i / Σ_j netChars_j

p_ik          = สัดส่วน netChars ของคน i ในช่วงเวลาที่ k (แบ่ง startedAt→dueAt เป็น 12 ช่วง)
consistency_i = (− Σ_k p_ik · log p_ik) / log 12

taskShare_i   = task ที่ closedBy = i / task ที่ปิดแล้วทั้งหมด   (ถ้าไม่มี task ปิดเลย = 1/n)

raw_i         = volume_i × (0.6 + 0.4 × consistency_i) + 0.2 × taskShare_i
                แล้ว normalize ให้ Σ raw = 1

fairShare     = 1 / n
aura_i        = round((raw_i − fairShare) × 10000)

gini          = Σ_i (2i − n − 1)·raw_i / (n · Σ_i raw_i)   เรียง raw น้อยไปมาก, i เริ่มที่ 1
elapsedRatio  = clamp01( (evaluatedAt − startedAt) / (dueAt − startedAt) )
panicIndex    = gini × elapsedRatio
alertFired    = gini > 0.45 && elapsedRatio > 0.5
dumpedAtEnd_i = netChars ของคน i ใน 15% ท้ายของ timeline / netChars ทั้งหมดของคน i > 0.6

consistency เป็นตัวคูณ ไม่ใช่ตัวบวก — ถ้าบวกตรงๆ คนที่เปิดเอกสารพิมพ์วันละคำจะได้ consistency เกือบเต็มโดยแทบไม่มีงานจริง การทำสม่ำเสมอควรขยายมูลค่าของแรงงานที่มีอยู่ ไม่ใช่เสกแรงงานจากศูนย์ พอเป็นตัวคูณ ใครที่ volume = 0 จะได้ศูนย์จากพจน์นี้เสมอ ไม่ว่าจะเข้าเอกสารบ่อยแค่ไหน (มี test บังคับข้อนี้)

tier ตัดสินจาก share ไม่ใช่จากเลขออร่าดิบshare_i = raw_i / (1/n) โดย share = 1.00 คือทำพอดีส่วนของตัวเอง ไม่ว่าทีมจะกี่คน เลขออร่ายังโชว์เหมือนเดิมเพราะมันคือตัวที่คนอ่าน

share tier gini verdict
≥ 2.00 🔥 CARRY < 0.25 LEGENDARY
1.25 – 1.99 SOLID 0.25 – 0.45 NORMAL
0.75 – 1.24 ⚖️ FAIR 0.45 – 0.6 TILTING
0.25 – 0.74 🚌 PASSENGER ≥ 0.6 ONE_MAN_ARMY
< 0.25 👻 GHOST

เหตุผล: ช่วงของ aura ขึ้นกับขนาดทีม คนที่ไม่แตะงานเลยได้ −10000/n ซึ่งคือ −3,333 ในทีม 3 คน แต่เป็นแค่ −500 ในทีม 20 คน ถ้าใช้ threshold คงที่ คนที่ไม่ทำอะไรในทีมใหญ่จะได้ป้าย "พอดีเป๊ะ" มี test บังคับไว้ที่ทีม 3 / 5 / 6 / 8 / 10 / 20 คน


ข้อจำกัดที่ต้องพูดให้ชัด

revision history จับ "งานที่พิมพ์" ได้ แต่จับ "งานที่คิด" ไม่ได้ คนที่ออกแบบโครงรายงานทั้งฉบับแล้วให้เพื่อนพิมพ์ตาม จะได้ออร่าติดลบ คนที่อ่านเปเปอร์สามสิบหน้าแล้วสรุปเหลือสองบรรทัด ระบบเห็นแค่สองบรรทัดนั้น

ตัวเลขออร่าจึงเป็น ข้อมูลตั้งต้นของบทสนทนา ไม่ใช่คำตัดสิน — ข้อจำกัดทั้ง 5 ข้อเขียนไว้เต็มที่ /methodology

ข้อจำกัดเชิงจริยธรรมที่บังคับใช้ในโค้ด

  • ไม่มี public leaderboard ข้ามกลุ่ม ทุกผลลัพธ์เห็นเฉพาะภายในกลุ่มนั้น
  • ระบบเตือนตอนยัง แก้ทัน (elapsedRatio > 0.5 คือผ่านครึ่งทางของ deadline) ไม่ใช่ประจานตอนจบ
  • ทุกหน้าที่แสดงผลมีปุ่มเข้า /methodology — ระบบที่ตัดสินคนต้องอธิบายตัวเองได้
  • ข้อความมีมทุกอันล้อ สถานการณ์ ไม่ใช่ล้อ คน ไม่มีคำหยาบหรือคำด่า
  • ไม่มี database ทั้งโปรเจกต์ ไม่มีการเก็บผลลัพธ์ของใครไว้

วางลิงก์เอกสารจริง (ต้องมี token)

โหมดปกติไม่ต้องต่ออะไรเลย และการลากไฟล์ .docx ที่เปิด Track Changes ก็ใช้ได้เต็มรูปแบบ โดยไม่ต้องมี token ส่วนการวางลิงก์ต้องมี token เพราะเป็นการเข้าถึงบัญชีของผู้ใช้เอง

cp .env.example .env.local
ลิงก์ env ที่ต้องมี scope / permission
Google Docs GOOGLE_OAUTH_ACCESS_TOKEN drive.readonly, drive.activity.readonly
SharePoint / OneDrive MICROSOFT_GRAPH_ACCESS_TOKEN Files.Read.All, Sites.Read.All

วิธีขอ token ทีละขั้นอยู่ใน .env.example และในหน้า /how-it-works#token

  • lib/adapters/googleDocs.tsrevisions.list → export text/plain → Myers diff คู่ติดกัน → ยกส่วนต่างให้ lastModifyingUser แล้วถ่วงด้วย Drive Activity API เมื่อมีหลายคนแก้ในช่วงเดียวกัน
  • lib/adapters/microsoftGraph.ts — encode sharing URL เป็น u!<base64url>/shares/{id}/driveItem/versions → โหลดเนื้อแต่ละเวอร์ชันเป็น .docx → ดึง w:t เป็น plain text → diff คู่ติดกัน → ยกส่วนต่างให้ lastModifiedBy ของเวอร์ชันนั้น

ถ้าไม่มี token ระบบจะไม่แสดงข้อมูลตัวอย่างแทน — มันจะบอกตรงๆ ว่าอ่านไม่ได้เพราะอะไร และต้องทำอะไรต่อ เพราะตัวเลขปลอมที่ดูเหมือนของจริงอันตรายกว่าไม่มีตัวเลข (ปุ่ม "ดู mock project" กับ ?demo= ยังเป็นทางเข้าข้อมูลตัวอย่างที่ผู้ใช้ตั้งใจกดเอง)


Stack

Next.js 15 (App Router) · TypeScript strict · Tailwind CSS v4 · Vitest กราฟทุกตัวเขียน SVG เอง (ไม่ใช้ chart library) · มีม render ด้วย Canvas API ฝั่ง client

Visual direction: เอกสารราชการโรงเรียน (Sarabun, พื้นกระดาษ, ปากกาแดง) ปะทะ คลิปตัดต่อออร่า (Anton, พื้นดำ, glow ม่วง–cyan) การพลิกจาก register หนึ่งไปอีก register คือ thesis ที่มองเห็นได้ — ข้อมูลน่าเบื่อ กลายเป็น มีม

โครงไฟล์

app/
  page.tsx               Landing — hero คือ live demo
  dashboard/page.tsx     ผลวิเคราะห์ (server component)
  methodology/page.tsx   สูตรทั้งหมด + ตารางแหล่งข้อมูล + ข้อจำกัด
  api/analyze/route.ts   วิเคราะห์จากลิงก์เอกสาร หรือคืน mock
lib/
  types.ts  scoring.ts  gini.ts  memeTemplates.ts  mockLoader.ts
  adapters/googleDocs.ts   revision history + Drive Activity → Edit[]
  adapters/wordDocx.ts     w:ins / w:del ใน .docx → Edit[]
  adapters/wordProject.ts  ตัวสร้าง Project (แยกไว้ให้ client ไม่ต้องโหลด jszip)
  adapters/github.ts       commits → Edit[]
components/
  AuraCard.tsx  PanicMeter.tsx  LorenzCurve.tsx  ContributionTimeline.tsx
  MemeCanvas.tsx  DashboardView.tsx  LiveHero.tsx  DocumentLinkForm.tsx
  CapabilityMatrix.tsx  SourceTable.tsx  DocxDropZone.tsx
scripts/
  generate-mock.mts           สร้าง mock JSON (mulberry32 + parse .docx จริง)
  generate-docx-fixtures.mts  สร้างไฟล์ .docx สำหรับเทสต์และตัวอย่าง
data/
  mock-projects.json     4 scenario (committed)
  github-fixture.json    ตัวอย่าง commit สำหรับ GitHub adapter
  gdocs-fixture.json     ตัวอย่าง revision + activity สำหรับ Google Docs adapter
public/samples/
  รายงานกลุ่ม-tracked.docx   ไฟล์ Word จริงที่เปิด Track Changes ให้โหลดไปลองเองได้
__tests__/
  scoring.test.ts        52 เคส
  adapters.test.ts       20 เคส
  wordDocx.test.ts       16 เคส
  fixtures/*.docx        ไฟล์ .docx จริง 4 แบบ (tracked / ไม่ tracked / anonymized / คนเดียว)

ทุก adapter มีหน้าตาเดียวกันคือ (source) => Promise<Edit[]> เพิ่มแหล่งข้อมูลใหม่จึงไม่ต้องแตะสูตรคำนวณ

About

วัดแรงงานจริงในงานกลุ่มจากร่องรอยการทำงาน แล้วยิงกลับเป็นมีม — ทำพอดีส่วนตัวเองได้ 0 ออร่า ทำน้อยติดลบ วัดความเหลื่อมล้ำด้วย Gini

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages