Skip to content

Commit

Permalink
zip files
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgebg committed Nov 28, 2023
1 parent 7c7b9b4 commit 1813756
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 87 deletions.
50 changes: 0 additions & 50 deletions .github/workflows/main.yml

This file was deleted.

Binary file added examples.zip
Binary file not shown.
40 changes: 31 additions & 9 deletions src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
<script lang="ts">
import { CHECKS, DEFAULT_CHECKS, PlagiarismDetection, getProjectsFromDirectory, type CheckType, type Project } from "./lib/plagiarism";
import { CHECKS, DEFAULT_CHECKS, PlagiarismDetection, getProjectsFromDirectory, getProjectsFromZipFile, type CheckType, type Project } from "./lib/plagiarism";
let dirHandler: FileSystemDirectoryHandle;
let projects: Project[] = []
let dirHandler: FileSystemDirectoryHandle | undefined;
let zipFile: File | undefined;
let projects: Project[];
let checks: CheckType[] = DEFAULT_CHECKS.slice();
const pickAndCheckFolder = async () => {
dirHandler = await showDirectoryPicker();
await checkFolder();
zipFile = undefined
if(dirHandler) {
await checkProjects();
}
}
const checkFolder = async() => {
projects = new PlagiarismDetection(await getProjectsFromDirectory(dirHandler)).detect(checks)
const checkZipFile = async (e) => {
[zipFile] = e.target.files;
dirHandler = undefined
if (zipFile) {
await checkProjects();
}
};
const checkProjects = async() => {
if (zipFile) {
projects = await getProjectsFromZipFile(zipFile)
} else if (dirHandler) {
projects = await getProjectsFromDirectory(dirHandler)
}
projects = new PlagiarismDetection(projects).detect(checks)
}
</script>

Expand All @@ -24,12 +43,15 @@
{#each CHECKS as check}
<label><input type="checkbox" bind:group={checks} value={check} /> {check}</label>
{/each}
<button on:click={() => checks = DEFAULT_CHECKS.slice()} >Reset</button>
<hr/>
<button on:click={pickAndCheckFolder} autofocus>Check folder</button>
<button on:click={checkFolder} disabled={!dirHandler}>Rerun check on folder</button>
<input on:change={checkZipFile} type="file" accept=".zip"/>
<hr/>
<button on:click={checkProjects} disabled={projects=== undefined}>Rerun check</button>
</fieldset>
{#if dirHandler}
<strong>Selected folder: {dirHandler.name}</strong>
{#if projects !== undefined}
<strong>Folder: {dirHandler?.name || zipFile?.name}</strong>
<hr/>
<strong>Plagiarism checks</strong>
<ul>
Expand Down
86 changes: 58 additions & 28 deletions src/lib/plagiarism.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { BlobReader, TextWriter, ZipReader, type Entry } from "@zip.js/zip.js";
import {
BlobReader,
BlobWriter,
TextWriter,
ZipReader,
type Entry,
} from "@zip.js/zip.js";
import equal from "deep-equal";
import type { Costume, Inputs, ScratchProjectObject, Sound } from "./scratch";

Expand All @@ -16,7 +22,6 @@ export type CheckType = (keyof ProjectIdentity)[number];

export type Project = {
path: string;
entry: FileSystemFileHandle;
projectJSONEntry: Entry;
projectObject: ScratchProjectObject;
projectIdentity: ProjectIdentity;
Expand All @@ -36,33 +41,13 @@ export const getProjectsFromDirectory = async (
const projects: Project[] = [];
for await (const entry of dirHandle.values()) {
if (entry.kind == "file" && entry.name.endsWith(".sb3")) {
const zipFileReader = new BlobReader(await entry.getFile());
const zipReader = new ZipReader(zipFileReader);
const zipEntries = await zipReader.getEntries();
const projectJSONEntry = zipEntries.find(
(e) => e.filename == "project.json"
const project = await getProject(
await entry.getFile(),
`${basePath}/${entry.name}`
);
if (projectJSONEntry !== undefined) {
const zipWriter = new TextWriter();
const projectJSONBlob = await projectJSONEntry.getData(zipWriter);
const projectObject = JSON.parse(projectJSONBlob);
const projectIdentity = computeIdentity(projectObject);
const project = {
path: basePath + entry.name,
entry,
projectJSONEntry,
projectObject,
projectIdentity,
plagiarists: [],
plagiarized: false,
};
if (project !== undefined) {
projects.push(project);
} else {
console.log(
`${basePath}/${entry.name} does't have a "project.json" file`
);
}
zipReader.close();
} else if (entry.kind == "directory") {
projects.push(
...(await getProjectsFromDirectory(entry, `${entry.name}/`))
Expand All @@ -72,6 +57,51 @@ export const getProjectsFromDirectory = async (
return projects;
};

export const getProjectsFromZipFile = async (file: File) => {
const projects: Project[] = [];
const zipFileReader = new BlobReader(file);
const zipReader = new ZipReader(zipFileReader);
const zipEntries = await zipReader.getEntries();
for await (const entry of zipEntries) {
if (entry.filename.endsWith(".sb3")) {
const zipWriter = new BlobWriter();
const project = await getProject(
await entry.getData(zipWriter),
entry.filename
);
if (project !== undefined) {
projects.push(project);
}
}
}
return projects;
};

const getProject = async (blob: Blob, path: string) => {
const zipFileReader = new BlobReader(blob);
const zipReader = new ZipReader(zipFileReader);
const zipEntries = await zipReader.getEntries();
const projectJSONEntry = zipEntries.find((e) => e.filename == "project.json");
if (projectJSONEntry !== undefined) {
const zipWriter = new TextWriter();
const projectJSONBlob = await projectJSONEntry.getData(zipWriter);
const projectObject = JSON.parse(projectJSONBlob);
const projectIdentity = computeIdentity(projectObject);
const project = {
path,
projectJSONEntry,
projectObject,
projectIdentity,
plagiarists: [],
plagiarized: false,
};
return project;
} else {
console.log(`${path} does't have a "project.json" file`);
}
zipReader.close();
};

export class ProjectIdentity {
constructor(
public opcodes: { id: string; opcode: string }[] = [],
Expand All @@ -83,7 +113,7 @@ export class ProjectIdentity {
public sounds: Sound[] = []
) {}
}
function computeIdentity(project: ScratchProjectObject): ProjectIdentity {
const computeIdentity = (project: ScratchProjectObject): ProjectIdentity => {
const identity = new ProjectIdentity();
for (const target of project.targets) {
const { direction, x, y, blocks, variables, costumes, sounds } = target;
Expand All @@ -104,7 +134,7 @@ function computeIdentity(project: ScratchProjectObject): ProjectIdentity {
identity[check].sort();
}
return identity;
}
};

class ProjectPlagiarist {
constructor(
Expand Down

0 comments on commit 1813756

Please sign in to comment.