-
Notifications
You must be signed in to change notification settings - Fork 523
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'hackclub:main' into main
- Loading branch information
Showing
180 changed files
with
14,964 additions
and
1,188 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,19 @@ | ||
Write a short description about your art piece, such as what your artwork represents and your experience drawing it! | ||
|
||
Next, make sure you go through **each item** in the following checklist. Delete this line after! | ||
|
||
- [ ] I have read the steps to [getting a blot](https://github.com/hackclub/blot/blob/main/docs/GET_A_BLOT.md) | ||
- [ ] I have read all the [submission requirements](https://blot.hackclub.com/submitting) and follow them | ||
- [ ] I am submitting art that... | ||
- [ ] is algorithmically generated (will change each time the program is run) | ||
- [ ] is drawable on a blot (fits in the work area & doesn't overlap too much) | ||
- [ ] is original (not copied from somewhere else) | ||
- [ ] is algorithmically generated (will meaningfully change each time the program is run) | ||
- [ ] is your own work that is not copied from somewhere else | ||
- [ ] is drawable through the editor(does not use outside tools) | ||
- [ ] doesn't call `Math.random()` (See the documentation on [randomness](https://github.com/hackclub/blot/blob/main/public/TOOLKIT.md#randomness)) | ||
- [ ] is drawable on a physical machine (doesn't have lines overlap more than 5 times) | ||
- [ ] is drawable on Blot itself (doesn't have lines overlap more than 5 times) | ||
- [ ] Doesn't overlap lines more than 5 times | ||
- [ ] Fits in the working area of 125x125mm | ||
- [ ] Doesn't rely on colour to look good | ||
|
||
If you used based your art on something else, used a tutorial, or remixed it from something else, please link it here: | ||
|
||
- [ ] Optional, if you used a tutorial or based your art on something else, please include the link here: | ||
- [ ] Optional, if you remixed this from something else, mention it here: | ||
Lastly, link a picture of your art piece here: |
File renamed without changes.
This file contains 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
File renamed without changes.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
submission: | ||
- changed-files: | ||
- any-glob-to-any-file: art/** |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from bs4 import BeautifulSoup | ||
import os | ||
import sys | ||
|
||
def extract_similarity_percentage(html_file, type): | ||
try: | ||
with open(html_file, 'r', encoding='utf-8') as file: | ||
soup = BeautifulSoup(file, 'html.parser') | ||
file_name_tag = soup.select_one(f"#{type}left > div > h4") | ||
if file_name_tag: | ||
percentage_text = file_name_tag.find("span", class_="text-secondary small").text.strip("()%") | ||
return int(percentage_text) | ||
else: | ||
return None | ||
except Exception as e: | ||
print(f"Error processing file {html_file}: {e}") | ||
return None | ||
|
||
def process_html_files(directory, threshold=50, noise_threshold=20): | ||
print("Processing HTML files for plagiarism results...") | ||
high_plagiarism_detected = False | ||
high_plagiarism_files = [] | ||
for filename in os.listdir(directory): | ||
if filename.endswith(".html"): | ||
file_path = os.path.join(directory, filename) | ||
percentage = extract_similarity_percentage(file_path,"structure") | ||
percentage_text = extract_similarity_percentage(file_path,"text") | ||
if percentage is not None and percentage >= threshold: | ||
print(f"High plagiarism detected - {filename.replace('.html', '')}: {percentage}%, {percentage_text}%") | ||
high_plagiarism_files.append(f"{filename.replace('.html', '')}: \n- Structure: {percentage}% \n- Text: {percentage_text}%") | ||
high_plagiarism_detected = True | ||
elif percentage is not None and percentage < noise_threshold: # Avoid huge action artifacts | ||
print(f"Removing plagiarism report with {filename.replace('.html', '')}: {percentage}%,{percentage_text}% score as noise") | ||
os.remove(file_path) | ||
return high_plagiarism_detected, high_plagiarism_files | ||
|
||
def write_to_markdown(file_path, lines): | ||
with open(file_path, 'w') as md_file: | ||
for line in lines: | ||
md_file.write(line + '\n') | ||
print(f"Markdown file written to {file_path}") | ||
|
||
def main(): | ||
if len(sys.argv) != 2: | ||
print("Incorrect number of arguments provided.") | ||
print("Usage: python extract_percentages.py <saved_dir_path>") | ||
sys.exit(1) | ||
|
||
saved_dir_path = sys.argv[1] | ||
high_plagiarism_detected, high_plagiarism_files = process_html_files(saved_dir_path) | ||
|
||
markdown_lines = ["# Plagiarism Report"] | ||
if high_plagiarism_detected: | ||
print("High plagiarism percentages detected.") | ||
markdown_lines.append("## Art overlap report:") | ||
markdown_lines.extend(high_plagiarism_files) | ||
write_to_markdown("plagiarism-report.md", markdown_lines) | ||
sys.exit(1) | ||
else: | ||
print("No high plagiarism percentages detected.") | ||
print("Plagiarism report generation completed.") | ||
if __name__ == "__main__": | ||
main() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import sys | ||
import subprocess | ||
import os | ||
import glob | ||
import shutil | ||
|
||
def run_compare50(single_file, directory, output_dir, saved_dir_base): | ||
try: | ||
if not os.path.exists(saved_dir_base): | ||
os.makedirs(saved_dir_base) | ||
print("Created base directory for saved files.") | ||
|
||
all_js_files = glob.glob(os.path.join(directory, "**/index.js")) | ||
total_files = len(all_js_files) | ||
current_file_number = 0 | ||
|
||
for file in all_js_files: | ||
current_file_number += 1 | ||
if os.path.abspath(file) == os.path.abspath(single_file): | ||
print(f"Skipping comparison for the same file: {file}") | ||
continue | ||
|
||
print(f"Processing file {current_file_number} of {total_files}: {file}") | ||
if os.path.exists(output_dir): | ||
shutil.rmtree(output_dir) | ||
print(f"Cleaned existing output directory: {output_dir}") | ||
|
||
command = [ | ||
"compare50", | ||
f'"{single_file}"', | ||
f'"{file}"', | ||
"--output", f'"{output_dir}"', | ||
"--max-file-size", str(1024 * 1024 * 100), | ||
"--passes", "structure text" | ||
] | ||
|
||
command_str = ' '.join(command) | ||
print(f"Running command: {command_str}") | ||
subprocess.run(command_str, shell=True, check=True) | ||
print("Compare50 command executed successfully.") | ||
|
||
match_file = os.path.join(output_dir, "match_1.html") | ||
|
||
if os.path.exists(match_file): | ||
new_filename = os.path.basename(os.path.normpath(os.path.dirname(file))) + '.html' | ||
saved_file_path = os.path.join(saved_dir_base, new_filename) | ||
print(f"Match found. Moving {match_file} to {saved_file_path}") | ||
shutil.move(match_file, saved_file_path) | ||
else: | ||
print(f"No match found for file: {file}") | ||
|
||
except subprocess.CalledProcessError as e: | ||
print(f"Error in running Compare50: {e}") | ||
except Exception as e: | ||
print(f"An error occurred: {e}") | ||
|
||
def main(): | ||
if len(sys.argv) != 5: | ||
print("Incorrect number of arguments provided.") | ||
print("Usage: python plagiarism_check.py <single_file> <directory> <output_dir> <saved_dir_base>") | ||
sys.exit(1) | ||
|
||
single_file = sys.argv[1] | ||
directory = sys.argv[2] | ||
output_dir = sys.argv[3] | ||
saved_dir_base = sys.argv[4] | ||
|
||
print(f"Starting plagiarism check with the following arguments:") | ||
print(f"Single file: {single_file}") | ||
print(f"Directory: {directory}") | ||
print(f"Output directory: {output_dir}") | ||
print(f"Saved directory base: {saved_dir_base}") | ||
|
||
# print(f"Listing all JavaScript files in directory '{directory}':") | ||
# for f in glob.glob(os.path.join(directory, "**/index.js")): | ||
# print(f) | ||
|
||
run_compare50(single_file, directory, output_dir, saved_dir_base) | ||
print("Plagiarism check completed.") | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
name: Plagiarism Checker | ||
|
||
permissions: | ||
contents: read | ||
issues: write | ||
pull-requests: write | ||
|
||
on: | ||
pull_request: | ||
paths: | ||
- "art/**/*.js" | ||
|
||
jobs: | ||
plagiarism-check: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: '3.10' | ||
|
||
- name: Install Compare50 && beautifulsoup4 | ||
run: pip install compare50 beautifulsoup4 | ||
|
||
- name: Get list of changed files | ||
id: changed-files | ||
run: | | ||
echo "Pull Request Base SHA: ${{ github.event.pull_request.base.sha }}" | ||
echo "Pull Request Head SHA: ${{ github.event.pull_request.head.sha }}" | ||
git diff --name-only --diff-filter=AM --find-renames --find-copies ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep 'art/.*\.js$' | xargs | ||
js_files=$(git diff --name-only --diff-filter=AM --find-renames --find-copies ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep 'art/.*\.js$' | xargs) | ||
echo "FILES=$js_files" >> $GITHUB_ENV | ||
- name: Run Plagiarism Detection Script | ||
if: env.FILES != '' | ||
run: python .github/scripts/plagiarism_check.py "${{ env.FILES }}" art output_dir saved_dir | ||
|
||
- name: Extract and Display Similarity Percentages | ||
run: python .github/scripts/extract_percentages.py saved_dir/ | ||
id: extract-percentages | ||
|
||
- name: Upload Compare50 Results as Artifacts | ||
if: always() | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: compare50-results | ||
path: saved_dir/ | ||
|
||
- name: Save PR number to file | ||
if: always() | ||
run: echo ${{ github.event.pull_request.number }} > pr_number.txt | ||
|
||
- name: Upload Plagiarism Report as Artifact | ||
if: always() && steps.extract-percentages.outcome == 'failure' | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: plagiarism-report | ||
path: plagiarism-report.md | ||
|
||
- name: Check for High Plagiarism Percentages | ||
if: always() && steps.extract-percentages.outcome == 'failure' | ||
run: echo "Plagiarism percentage over threshold detected." | ||
|
||
- name: Post Markdown as Comment | ||
if: always() && steps.extract-percentages.outcome == 'failure' | ||
uses: actions/github-script@v7 | ||
with: | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
script: | | ||
const fs = require('fs'); | ||
console.log("Reading pr number...") | ||
const prNumber = fs.readFileSync('./pr_number.txt', 'utf8').trim(); | ||
console.log("Reading report...") | ||
const markdownContent = fs.readFileSync('./plagiarism-report.md', 'utf8'); | ||
console.log("Posting the Markdown content as a comment..."); | ||
const commentResponse = await github.rest.issues.createComment({ | ||
issue_number: prNumber, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
body: markdownContent | ||
}); | ||
console.log(`Comment posted successfully: ${commentResponse.data.html_url}`); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
name: "Label Art submissions" | ||
|
||
on: | ||
- pull_request_target | ||
|
||
jobs: | ||
triage: | ||
permissions: | ||
contents: read | ||
pull-requests: write | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/labeler@v5 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import express from 'express'; | ||
|
||
import signUpEmail from "../backend/api/signUpEmail.js"; | ||
import checkSignIn from "../backend/api/checkSignIn.js"; | ||
import saveFile from "../backend/api/saveFile.js"; | ||
import getUser from "../backend/api/getUser.js"; | ||
import submitCode from "../backend/api/submitCode.js"; | ||
import getFiles from "../backend/api/getFiles.js"; | ||
import createShareLink from "../backend/api/createShareLink.js"; | ||
import logout from "../backend/api/logout.js"; | ||
import deleteFile from "../backend/api/deleteFile.js"; | ||
|
||
import { supabase } from "../backend/api/supabase.js"; | ||
|
||
import { fileURLToPath } from 'url'; | ||
import { dirname, join } from 'path'; | ||
import path from 'path'; | ||
|
||
const app = express(); | ||
|
||
app.use(express.json()); | ||
|
||
app.post('/signUpEmail', signUpEmail); | ||
app.post('/check-signed-in', checkSignIn); | ||
app.post('/get-files', getFiles); | ||
app.post('/save-file', saveFile); | ||
app.post('/delete-file', deleteFile); | ||
app.post('/logout', logout); | ||
app.post('/get-user', getUser); | ||
app.post('/submit-code', submitCode); | ||
app.post('/create-share-link', createShareLink); | ||
app.get('/read-share-link', async (req, res) => { | ||
const { id } = req.query; | ||
|
||
try { | ||
|
||
console.log("check in database"); | ||
// check if email is in database | ||
let { data: file, error: fileError } = await supabase | ||
.from('share_link') | ||
.select('*') | ||
.eq('id', id) | ||
.single(); | ||
|
||
if (fileError && fileError.message !== 'No rows found') { | ||
res.send("no share link here"); | ||
return; | ||
} | ||
|
||
res.send(file.content); | ||
} catch (error) { | ||
res.status(500).send({ error: error.message }); | ||
} | ||
}); | ||
app.get('/assembly', (req, res) => { | ||
res.redirect('https://github.com/hackclub/blot/blob/main/docs/assembly/ASSEMBLY.md'); | ||
}); | ||
app.get('/welcome-qr-code', (req, res) => { | ||
res.redirect('/assembly'); | ||
}); | ||
|
||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = dirname(__filename); | ||
|
||
app.use(express.static(join(__dirname, '../dist'))); | ||
|
||
app.use((req, res, next) => { | ||
let pathName = req.path; | ||
|
||
if (pathName === "/") pathName = "/index"; | ||
|
||
if (req.path.indexOf('.') === -1) { | ||
const file = path.join(__dirname, '../dist', `${pathName}.html`); | ||
res.sendFile(file, (err) => { | ||
if (err) next(); | ||
}); | ||
} else { | ||
next(); | ||
} | ||
}); | ||
|
||
app.use((req, res, next) => { | ||
const file = path.join(__dirname, '../dist', `404.html`); | ||
res.status(404).sendFile(file); | ||
}); | ||
export default app; |
Oops, something went wrong.