generated from polymech/site-template
howtos, basic shit ;
This commit is contained in:
parent
511f9c9add
commit
930d5c207c
File diff suppressed because one or more lines are too long
177
src/model/howto-model.ts
Normal file
177
src/model/howto-model.ts
Normal file
@ -0,0 +1,177 @@
|
||||
export const ITEM_TYPE = 'howto'
|
||||
////////////////////////////////
|
||||
//
|
||||
// Interfaces - Old
|
||||
export interface IHowto {
|
||||
_createdBy: string
|
||||
mentions: any[]
|
||||
_deleted: boolean
|
||||
fileLink: string
|
||||
slug: string
|
||||
_modified: string
|
||||
previousSlugs: string[]
|
||||
_created: string
|
||||
description: string
|
||||
votedUsefulBy: string[]
|
||||
creatorCountry: string
|
||||
total_downloads: number
|
||||
title: string
|
||||
time: string
|
||||
files: any[]
|
||||
category: IOACategory
|
||||
difficulty_level: string
|
||||
_id: string
|
||||
tags?: IOATag[]
|
||||
total_views: number
|
||||
_contentModifiedTimestamp: string
|
||||
cover_image: Image
|
||||
comments: any[]
|
||||
moderatorFeedback: string
|
||||
steps: Step[]
|
||||
moderation: string
|
||||
user?: User
|
||||
}
|
||||
|
||||
export interface Tags {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
||||
export interface CoverImage {
|
||||
name: string
|
||||
downloadUrl: string
|
||||
type: string
|
||||
fullPath: string
|
||||
updated: string
|
||||
size: number
|
||||
timeCreated: string
|
||||
contentType: string
|
||||
}
|
||||
|
||||
export interface Step {
|
||||
title: string
|
||||
text: string
|
||||
images: Image[]
|
||||
_animationKey: string
|
||||
}
|
||||
export interface Image {
|
||||
updated: string
|
||||
size: number
|
||||
fullPath: string
|
||||
timeCreated: string
|
||||
name: string
|
||||
downloadUrl: string
|
||||
contentType: string
|
||||
type: string
|
||||
src: string,
|
||||
alt: string
|
||||
}
|
||||
|
||||
/// Taxonomy
|
||||
export interface IOACategory {
|
||||
_created: string
|
||||
_id: string
|
||||
_deleted: boolean
|
||||
label: string
|
||||
_modified: string
|
||||
}
|
||||
|
||||
export interface IOATag {
|
||||
categories: string[]
|
||||
image: string
|
||||
_created: string
|
||||
_deleted: boolean
|
||||
label: string
|
||||
_createdBy: string
|
||||
_modified: string
|
||||
_id: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
_modified: string
|
||||
_id: string
|
||||
subType: string
|
||||
moderation: string
|
||||
_deleted: boolean
|
||||
verified: boolean
|
||||
type: string
|
||||
location: Location
|
||||
_created: string
|
||||
geo: Geo
|
||||
data: Data
|
||||
detail: Detail
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
lat: number
|
||||
lng: number
|
||||
}
|
||||
|
||||
export interface Geo {
|
||||
latitude: number
|
||||
lookupSource: string
|
||||
longitude: number
|
||||
localityLanguageRequested: string
|
||||
continent: string
|
||||
continentCode: string
|
||||
countryName: string
|
||||
countryCode: string
|
||||
principalSubdivision: string
|
||||
principalSubdivisionCode: string
|
||||
city: string
|
||||
locality: string
|
||||
postcode: string
|
||||
plusCode: string
|
||||
localityInfo: LocalityInfo
|
||||
}
|
||||
|
||||
export interface LocalityInfo {
|
||||
administrative: Administrative[]
|
||||
informative: Informative[]
|
||||
}
|
||||
|
||||
export interface Administrative {
|
||||
name: string
|
||||
description: string
|
||||
isoName?: string
|
||||
order: number
|
||||
adminLevel: number
|
||||
isoCode?: string
|
||||
wikidataId: string
|
||||
geonameId: number
|
||||
}
|
||||
|
||||
export interface Informative {
|
||||
name: string
|
||||
description: string
|
||||
isoName?: string
|
||||
order: number
|
||||
isoCode?: string
|
||||
wikidataId?: string
|
||||
geonameId?: number
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
urls: Url[]
|
||||
description: string
|
||||
services: Service[]
|
||||
title: string
|
||||
images: any[]
|
||||
}
|
||||
|
||||
export interface Url {
|
||||
name: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface Service {
|
||||
welding: boolean
|
||||
assembling: boolean
|
||||
machining: boolean
|
||||
electronics: boolean
|
||||
molds: boolean
|
||||
}
|
||||
|
||||
export interface Detail {
|
||||
services: any[]
|
||||
urls: any[]
|
||||
}
|
||||
11
src/model/howto-workflow.sh
Normal file
11
src/model/howto-workflow.sh
Normal file
@ -0,0 +1,11 @@
|
||||
kbotd --preferences ./todos-workflow.md \
|
||||
--include=./howto-model.ts \
|
||||
--include=./howto_sample.json \
|
||||
--disable=terminal,git,npm,user,interact,search,email,web \
|
||||
--disableTools=read_file,read_files,list_files,file_exists,web \
|
||||
--model=anthropic/claude-3.7-sonnet:thinking \
|
||||
--mode=completion \
|
||||
--filters=code \
|
||||
--dst=./json-ld-howto.ts
|
||||
|
||||
|
||||
@ -5,10 +5,12 @@ import { sync as read } from '@polymech/fs/read'
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { sync as mkdir } from '@polymech/fs/dir'
|
||||
import { sync as rm } from '@polymech/fs/remove'
|
||||
|
||||
import { IHowto, Image } from './howto-model.js';
|
||||
import type { Loader, LoaderContext } from 'astro/loaders'
|
||||
import { sanitizeFilename } from "@polymech/fs/utils"
|
||||
import { filter as language } from "@/base/kbot.js";
|
||||
export * from './howto-model.js'
|
||||
|
||||
import {
|
||||
HOWTO_FILES_WEB,
|
||||
HOWTO_FILES_ABS,
|
||||
@ -35,8 +37,8 @@ export const ITEM_TYPE = 'howto'
|
||||
//export const load = () => get(`${HOWTO_ROOT()}/${HOWTO_GLOB}`, HOWTO_ROOT(), ITEM_TYPE)
|
||||
export const item_path = (item: any) => `${HOWTO_ROOT()}/${item.data.slug}`
|
||||
|
||||
const blacklist = [];
|
||||
const blacklist_mafia = ['precious-plastic', 'fair-enough', 'mad-plastic-labs', 'the-flipflopi', 'easymoulds', 'plasticpreneur', 'sustainable-design-studio'];
|
||||
const blacklist_ = [];
|
||||
const blacklist = ['precious-plastic', 'fair-enough', 'mad-plastic-labs', 'the-flipflopi', 'easymoulds', 'plasticpreneur', 'sustainable-design-studio'];
|
||||
|
||||
const download = async (url, outputPath) => {
|
||||
const stream = createWriteStream(outputPath);
|
||||
@ -141,7 +143,7 @@ export const howtos = async () => {
|
||||
label: 'uncategorized'
|
||||
}
|
||||
})
|
||||
howtos = howtos.filter((h) => {
|
||||
howtos = howtos.filter((h:IHowto) => {
|
||||
return h.steps.length > 0 && !blacklist.includes(h._createdBy);
|
||||
});
|
||||
return howtos
|
||||
@ -297,179 +299,3 @@ export async function applyFilters(text: string): Promise<string> {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//
|
||||
// Interfaces - Old
|
||||
export interface IHowto {
|
||||
_createdBy: string
|
||||
mentions: any[]
|
||||
_deleted: boolean
|
||||
fileLink: string
|
||||
slug: string
|
||||
_modified: string
|
||||
previousSlugs: string[]
|
||||
_created: string
|
||||
description: string
|
||||
votedUsefulBy: string[]
|
||||
creatorCountry: string
|
||||
total_downloads: number
|
||||
title: string
|
||||
time: string
|
||||
files: any[]
|
||||
category: IOACategory
|
||||
difficulty_level: string
|
||||
_id: string
|
||||
tags?: Tags
|
||||
total_views: number
|
||||
_contentModifiedTimestamp: string
|
||||
cover_image: Image
|
||||
comments: any[]
|
||||
moderatorFeedback: string
|
||||
steps: Step[]
|
||||
moderation: string
|
||||
user?: User
|
||||
}
|
||||
|
||||
export interface Tags {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
||||
export interface CoverImage {
|
||||
name: string
|
||||
downloadUrl: string
|
||||
type: string
|
||||
fullPath: string
|
||||
updated: string
|
||||
size: number
|
||||
timeCreated: string
|
||||
contentType: string
|
||||
}
|
||||
|
||||
export interface Step {
|
||||
title: string
|
||||
text: string
|
||||
images: Image[]
|
||||
_animationKey: string
|
||||
}
|
||||
export interface Image {
|
||||
updated: string
|
||||
size: number
|
||||
fullPath: string
|
||||
timeCreated: string
|
||||
name: string
|
||||
downloadUrl: string
|
||||
contentType: string
|
||||
type: string
|
||||
src: string,
|
||||
alt: string
|
||||
}
|
||||
|
||||
/// Taxonomy
|
||||
export interface IOACategory {
|
||||
_created: string
|
||||
_id: string
|
||||
_deleted: boolean
|
||||
label: string
|
||||
_modified: string
|
||||
}
|
||||
|
||||
export interface IOATag {
|
||||
categories: string[]
|
||||
image: string
|
||||
_created: string
|
||||
_deleted: boolean
|
||||
label: string
|
||||
_createdBy: string
|
||||
_modified: string
|
||||
_id: string
|
||||
}
|
||||
|
||||
export interface User {
|
||||
_modified: string
|
||||
_id: string
|
||||
subType: string
|
||||
moderation: string
|
||||
_deleted: boolean
|
||||
verified: boolean
|
||||
type: string
|
||||
location: Location
|
||||
_created: string
|
||||
geo: Geo
|
||||
data: Data
|
||||
detail: Detail
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
lat: number
|
||||
lng: number
|
||||
}
|
||||
|
||||
export interface Geo {
|
||||
latitude: number
|
||||
lookupSource: string
|
||||
longitude: number
|
||||
localityLanguageRequested: string
|
||||
continent: string
|
||||
continentCode: string
|
||||
countryName: string
|
||||
countryCode: string
|
||||
principalSubdivision: string
|
||||
principalSubdivisionCode: string
|
||||
city: string
|
||||
locality: string
|
||||
postcode: string
|
||||
plusCode: string
|
||||
localityInfo: LocalityInfo
|
||||
}
|
||||
|
||||
export interface LocalityInfo {
|
||||
administrative: Administrative[]
|
||||
informative: Informative[]
|
||||
}
|
||||
|
||||
export interface Administrative {
|
||||
name: string
|
||||
description: string
|
||||
isoName?: string
|
||||
order: number
|
||||
adminLevel: number
|
||||
isoCode?: string
|
||||
wikidataId: string
|
||||
geonameId: number
|
||||
}
|
||||
|
||||
export interface Informative {
|
||||
name: string
|
||||
description: string
|
||||
isoName?: string
|
||||
order: number
|
||||
isoCode?: string
|
||||
wikidataId?: string
|
||||
geonameId?: number
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
urls: Url[]
|
||||
description: string
|
||||
services: Service[]
|
||||
title: string
|
||||
images: any[]
|
||||
}
|
||||
|
||||
export interface Url {
|
||||
name: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface Service {
|
||||
welding: boolean
|
||||
assembling: boolean
|
||||
machining: boolean
|
||||
electronics: boolean
|
||||
molds: boolean
|
||||
}
|
||||
|
||||
export interface Detail {
|
||||
services: any[]
|
||||
urls: any[]
|
||||
}
|
||||
|
||||
@ -1,339 +0,0 @@
|
||||
{
|
||||
"_createdBy": "gus-merckel",
|
||||
"mentions": [],
|
||||
"_deleted": false,
|
||||
"fileLink": "",
|
||||
"slug": "cut-out-shapes-out-of-plastic-sheets-with-a-cnc-",
|
||||
"_modified": "2023-10-27T18:09:36.519Z",
|
||||
"previousSlugs": [
|
||||
"cut-out-shapes-out-of-plastic-sheets-with-a-cnc-"
|
||||
],
|
||||
"_created": "2023-08-23T18:20:09.098Z",
|
||||
"description": "In this how to, I will show you our process to cut HDPE Sheets using a X-Carve CNC.\n\nHere is the full video in spanish with subtitles https://www.youtube.com/watch?v=4LrrFz802To ",
|
||||
"votedUsefulBy": [
|
||||
"sigolene",
|
||||
"mattia",
|
||||
"uillinoispreciousplastics"
|
||||
],
|
||||
"creatorCountry": "mx",
|
||||
"total_downloads": 0,
|
||||
"title": "Cut out shapes out of plastic sheets with a CNC ",
|
||||
"time": "< 5 hours",
|
||||
"files": [],
|
||||
"difficulty_level": "Medium",
|
||||
"_id": "038gjWgLjiyYknbEjDeI",
|
||||
"tags": [
|
||||
"HDPE"
|
||||
],
|
||||
"total_views": 232,
|
||||
"_contentModifiedTimestamp": "2023-08-23T18:20:09.098Z",
|
||||
"cover_image": {
|
||||
"name": "IMG_20200605_142311.jpg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2F038gjWgLjiyYknbEjDeI%2FIMG_20200605_142311.jpg?alt=media&token=c272c174-1adc-45af-967b-771adce7295d",
|
||||
"type": "image/jpeg",
|
||||
"fullPath": "uploads/howtos/038gjWgLjiyYknbEjDeI/IMG_20200605_142311.jpg",
|
||||
"updated": "2021-04-05T15:09:00.605Z",
|
||||
"size": 124661,
|
||||
"timeCreated": "2021-04-05T15:09:00.605Z",
|
||||
"contentType": "image/jpeg"
|
||||
},
|
||||
"comments": [],
|
||||
"moderatorFeedback": "",
|
||||
"steps": [
|
||||
{
|
||||
"title": "Measure the plastic sheet",
|
||||
"text": "For this step we need to measure our plastic sheet: Height, Width and Thickness. Our X-Carve machine works with the CAM Software EASEL, for me, the easiest software for CNC milling out there. \n\nThe cool thing about Easel (https://easel.inventables.com/) is that you can \"simulate\" your actual material and THEY EVEN HAVE HDPE 2-Colors in their cutting material lists!!\n\n\n",
|
||||
"images": [
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/1.jpg",
|
||||
"name": "1.jpg",
|
||||
"size": 74095,
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:05.766Z",
|
||||
"contentType": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F1.jpg?alt=media&token=293d733d-05a5-494a-9340-47f4564f1939",
|
||||
"updated": "2021-03-26T19:42:05.766Z",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/1.jpg",
|
||||
"alt": "1.jpg"
|
||||
},
|
||||
{
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:05.669Z",
|
||||
"updated": "2021-03-26T19:42:05.669Z",
|
||||
"size": 69665,
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F2.jpg?alt=media&token=004f50f1-97ac-4df4-9ba9-f463aa4cbca3",
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/2.jpg",
|
||||
"name": "2.jpg",
|
||||
"type": "image/jpeg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/2.jpg",
|
||||
"alt": "2.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "unique1"
|
||||
},
|
||||
{
|
||||
"text": "Using the CNC clamps from the X-Carve, secure the sheet to the table, ",
|
||||
"_animationKey": "unique2",
|
||||
"images": [
|
||||
{
|
||||
"updated": "2021-03-26T19:42:06.249Z",
|
||||
"size": 55544,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/3.jpg",
|
||||
"timeCreated": "2021-03-26T19:42:06.249Z",
|
||||
"name": "3.jpg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F3.jpg?alt=media&token=0b9c1914-1c75-429e-b34a-1e2b3706edef",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/3.jpg",
|
||||
"alt": "3.jpg"
|
||||
}
|
||||
],
|
||||
"title": "Secure sheet "
|
||||
},
|
||||
{
|
||||
"title": "Choosing a file to cut ",
|
||||
"text": "Now we go to our illustrator, such as Inkscape to design a vector file or download and open source one frome https://thenounproject.com/.\n\nWe download the SVG file, which is an open source vector format and import it to Easel. \n",
|
||||
"images": [
|
||||
{
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F4.jpg?alt=media&token=1cd2d49d-9335-4bb1-ac2a-e625322ca604",
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:06.727Z",
|
||||
"updated": "2021-03-26T19:42:06.727Z",
|
||||
"name": "4.jpg",
|
||||
"size": 42952,
|
||||
"type": "image/jpeg",
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/4.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/4.jpg",
|
||||
"alt": "4.jpg"
|
||||
},
|
||||
{
|
||||
"size": 69255,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/5.jpg",
|
||||
"updated": "2021-03-26T19:42:06.833Z",
|
||||
"timeCreated": "2021-03-26T19:42:06.833Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F5.jpg?alt=media&token=7cca786a-7d47-43bb-900b-b8d101c276b4",
|
||||
"name": "5.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/5.jpg",
|
||||
"alt": "5.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "unique3"
|
||||
},
|
||||
{
|
||||
"text": "Now with the file we can choose the width we want to carve/cut and then we go to cut and start the wizzard:\n- We check that the sheet is fixed.\n- We also specify the cutting bit, we are using a 1/8 flat flute bit. \n- We tell the machine where the coordinate 0-0 is, which we always choose as the down left corner.\n- We raise the bit, turn on the Router!!!\n\nAND PUM THE MAGIC BEGINS!!",
|
||||
"title": "Follow the cutting Wizzard",
|
||||
"images": [
|
||||
{
|
||||
"timeCreated": "2021-03-26T19:42:07.493Z",
|
||||
"size": 72226,
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/6.jpg",
|
||||
"updated": "2021-03-26T19:42:07.493Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F6.jpg?alt=media&token=ba7195dd-7771-435f-a188-057457697332",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"name": "6.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/6.jpg",
|
||||
"alt": "6.jpg"
|
||||
},
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/7.jpg",
|
||||
"size": 52424,
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F7.jpg?alt=media&token=a3d5820c-cfe2-484e-8f76-f861ab8b756d",
|
||||
"contentType": "image/jpeg",
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:07.308Z",
|
||||
"updated": "2021-03-26T19:42:07.308Z",
|
||||
"name": "7.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/7.jpg",
|
||||
"alt": "7.jpg"
|
||||
},
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/8.jpg",
|
||||
"name": "8.jpg",
|
||||
"type": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:07.346Z",
|
||||
"size": 55264,
|
||||
"contentType": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F8.jpg?alt=media&token=1c9816d7-3a99-4f41-8d3c-acc2670240f6",
|
||||
"updated": "2021-03-26T19:42:07.346Z",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/8.jpg",
|
||||
"alt": "8.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "uniquenisc2v"
|
||||
},
|
||||
{
|
||||
"text": "You take now your glasses or object and postprocess them and of course show it to your friends, family and so on.\n\n\n",
|
||||
"images": [
|
||||
{
|
||||
"fullPath": "uploads/howtos/pbo0Pe44aTngvlD04kGf/9.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-03-26T19:42:08.147Z",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2Fpbo0Pe44aTngvlD04kGf%2F9.jpg?alt=media&token=4dcfe37d-e1ad-41e5-a590-40b4c37c5e1a",
|
||||
"name": "9.jpg",
|
||||
"updated": "2021-03-26T19:42:08.147Z",
|
||||
"type": "image/jpeg",
|
||||
"size": 82214,
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/9.jpg",
|
||||
"alt": "9.jpg"
|
||||
}
|
||||
],
|
||||
"_animationKey": "uniquesgl34",
|
||||
"title": "Post-production and show case"
|
||||
},
|
||||
{
|
||||
"_animationKey": "uniquem4y0yi",
|
||||
"title": "Hack it and try it yourself",
|
||||
"text": "You can try this project with other types of CNC machines, even manual Routers or manual saw, as I did on this video: https://youtu.be/gxkcffQD3eQ, but the important thing is that you share what you do and help this community to grow!!!\n\nShare your ideas and comments!",
|
||||
"images": [
|
||||
{
|
||||
"contentType": "image/jpeg",
|
||||
"timeCreated": "2021-04-05T15:09:01.445Z",
|
||||
"fullPath": "uploads/howtos/038gjWgLjiyYknbEjDeI/IMG_20200605_142311.jpg",
|
||||
"type": "image/jpeg",
|
||||
"downloadUrl": "https://firebasestorage.googleapis.com/v0/b/onearmyworld.appspot.com/o/uploads%2Fhowtos%2F038gjWgLjiyYknbEjDeI%2FIMG_20200605_142311.jpg?alt=media&token=f94152ff-f923-4054-a3ad-d8ec588856fa",
|
||||
"size": 124661,
|
||||
"updated": "2021-04-05T15:09:01.445Z",
|
||||
"name": "IMG_20200605_142311.jpg",
|
||||
"src": "/resources/howtos/cut-out-shapes-out-of-plastic-sheets-with-a-cnc-/IMG_20200605_142311.jpg",
|
||||
"alt": "IMG_20200605_142311.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"moderation": "accepted",
|
||||
"user": {
|
||||
"_modified": "2024-01-08T13:28:33.484Z",
|
||||
"_id": "gus-merckel",
|
||||
"subType": "mix",
|
||||
"moderation": "accepted",
|
||||
"_deleted": false,
|
||||
"verified": false,
|
||||
"type": "workspace",
|
||||
"location": {
|
||||
"lat": 19.3935,
|
||||
"lng": -99.1656
|
||||
},
|
||||
"_created": "2024-01-08T13:28:33.484Z",
|
||||
"geo": {
|
||||
"latitude": 19.3935,
|
||||
"lookupSource": "coordinates",
|
||||
"longitude": -99.1656,
|
||||
"localityLanguageRequested": "en",
|
||||
"continent": "North America",
|
||||
"continentCode": "NA",
|
||||
"countryName": "Mexico",
|
||||
"countryCode": "MX",
|
||||
"principalSubdivision": "Ciudad de Mexico",
|
||||
"principalSubdivisionCode": "MX-CMX",
|
||||
"city": "Mexico City",
|
||||
"locality": "Benito Juarez",
|
||||
"postcode": "03103",
|
||||
"plusCode": "76F29RVM+CQ",
|
||||
"localityInfo": {
|
||||
"administrative": [
|
||||
{
|
||||
"name": "Mexico",
|
||||
"description": "country in North America",
|
||||
"isoName": "Mexico",
|
||||
"order": 2,
|
||||
"adminLevel": 2,
|
||||
"isoCode": "MX",
|
||||
"wikidataId": "Q96",
|
||||
"geonameId": 3996063
|
||||
},
|
||||
{
|
||||
"name": "Mexico City",
|
||||
"description": "capital and largest city of Mexico",
|
||||
"order": 5,
|
||||
"adminLevel": 4,
|
||||
"wikidataId": "Q1489",
|
||||
"geonameId": 3530597
|
||||
},
|
||||
{
|
||||
"name": "Ciudad de Mexico",
|
||||
"description": "capital and largest city of Mexico",
|
||||
"isoName": "Ciudad de Mexico",
|
||||
"order": 6,
|
||||
"adminLevel": 4,
|
||||
"isoCode": "MX-CMX",
|
||||
"wikidataId": "Q1489",
|
||||
"geonameId": 3527646
|
||||
},
|
||||
{
|
||||
"name": "Benito Juarez",
|
||||
"description": "territorial demarcation of the Mexico City in Mexico",
|
||||
"order": 7,
|
||||
"adminLevel": 6,
|
||||
"wikidataId": "Q2356998",
|
||||
"geonameId": 3827406
|
||||
}
|
||||
],
|
||||
"informative": [
|
||||
{
|
||||
"name": "North America",
|
||||
"description": "continent and northern subcontinent of the Americas",
|
||||
"isoName": "North America",
|
||||
"order": 1,
|
||||
"isoCode": "NA",
|
||||
"wikidataId": "Q49",
|
||||
"geonameId": 6255149
|
||||
},
|
||||
{
|
||||
"name": "America/Mexico_City",
|
||||
"description": "time zone",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"name": "Greater Mexico City",
|
||||
"description": "geographical object",
|
||||
"order": 4,
|
||||
"wikidataId": "Q665894"
|
||||
},
|
||||
{
|
||||
"name": "03103",
|
||||
"description": "postal code",
|
||||
"order": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"urls": [
|
||||
{
|
||||
"name": "Email",
|
||||
"url": "mailto:gustavomerckel@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Facebook",
|
||||
"url": "https://www.facebook.com/pl%c3%a1stico-chido-110888520718193"
|
||||
},
|
||||
{
|
||||
"name": "sponsor the work",
|
||||
"url": "https://www.patreon.com/one_army"
|
||||
}
|
||||
],
|
||||
"description": "Plástico Chido builds and modifies the PP machines, and also experiments with CNC and Laser Cut",
|
||||
"services": [
|
||||
{
|
||||
"welding": false,
|
||||
"assembling": false,
|
||||
"machining": false,
|
||||
"electronics": false,
|
||||
"molds": false
|
||||
}
|
||||
],
|
||||
"title": "Plástico Chido",
|
||||
"images": []
|
||||
},
|
||||
"detail": {
|
||||
"services": [],
|
||||
"urls": []
|
||||
}
|
||||
},
|
||||
"category": {
|
||||
"label": "uncategorized"
|
||||
}
|
||||
}
|
||||
94
src/model/json-ld-howto.ts
Normal file
94
src/model/json-ld-howto.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { IHowto } from './howto-model.js';
|
||||
|
||||
export const toJSONLD = (howto: IHowto) => {
|
||||
const jsonLD: any = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "HowTo",
|
||||
"name": howto.title,
|
||||
"description": howto.description,
|
||||
};
|
||||
|
||||
if (howto.cover_image?.downloadUrl) {
|
||||
jsonLD.image = howto.cover_image.downloadUrl;
|
||||
}
|
||||
|
||||
if (howto.time) {
|
||||
let duration = "";
|
||||
const timeMatch = howto.time.match(/(\d+)\s*(hour|minute|second|day|week|month|year)/i);
|
||||
if (timeMatch) {
|
||||
const value = parseInt(timeMatch[1], 10);
|
||||
const unit = timeMatch[2].toLowerCase();
|
||||
|
||||
switch(unit) {
|
||||
case 'hour': duration = `PT${value}H`; break;
|
||||
case 'minute': duration = `PT${value}M`; break;
|
||||
case 'second': duration = `PT${value}S`; break;
|
||||
case 'day': duration = `P${value}D`; break;
|
||||
case 'week': duration = `P${value}W`; break;
|
||||
case 'month': duration = `P${value}M`; break;
|
||||
case 'year': duration = `P${value}Y`; break;
|
||||
}
|
||||
} else if (howto.time.includes('<')) {
|
||||
const lessTimeMatch = howto.time.match(/< (\d+)\s*(hour|minute|second|day|week|month|year)/i);
|
||||
if (lessTimeMatch) {
|
||||
const value = parseInt(lessTimeMatch[1], 10);
|
||||
const unit = lessTimeMatch[2].toLowerCase();
|
||||
|
||||
switch(unit) {
|
||||
case 'hour': duration = `PT${value - 1}H`; break;
|
||||
case 'minute': duration = `PT${value - 1}M`; break;
|
||||
case 'second': duration = `PT${value - 1}S`; break;
|
||||
case 'day': duration = `P${value - 1}D`; break;
|
||||
case 'week': duration = `P${value - 1}W`; break;
|
||||
case 'month': duration = `P${value - 1}M`; break;
|
||||
case 'year': duration = `P${value - 1}Y`; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (duration) {
|
||||
jsonLD.totalTime = duration;
|
||||
}
|
||||
}
|
||||
|
||||
if (howto.difficulty_level) {
|
||||
jsonLD.difficultyLevel = howto.difficulty_level;
|
||||
}
|
||||
|
||||
if (howto.category?.label) {
|
||||
jsonLD.category = howto.category.label;
|
||||
}
|
||||
|
||||
if (howto.tags && howto.tags.length > 0) {
|
||||
const keywords = howto.tags.map(tag =>
|
||||
typeof tag === 'string' ? tag : tag?.label || ''
|
||||
).filter(Boolean).join(', ');
|
||||
|
||||
if (keywords) {
|
||||
jsonLD.keywords = keywords;
|
||||
}
|
||||
}
|
||||
|
||||
if (howto.steps && howto.steps.length > 0) {
|
||||
jsonLD.step = howto.steps.map((step, index) => {
|
||||
const stepObj: any = {
|
||||
"@type": "HowToStep",
|
||||
"name": step.title,
|
||||
"text": step.text,
|
||||
"position": index + 1
|
||||
};
|
||||
|
||||
if (step.images && step.images.length > 0) {
|
||||
if (step.images.length === 1) {
|
||||
stepObj.image = step.images[0].downloadUrl;
|
||||
} else {
|
||||
stepObj.image = step.images.map(image => image.downloadUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return stepObj;
|
||||
});
|
||||
}
|
||||
|
||||
return jsonLD;
|
||||
};
|
||||
9
src/model/todos-workflow.md
Normal file
9
src/model/todos-workflow.md
Normal file
@ -0,0 +1,9 @@
|
||||
## Todos
|
||||
|
||||
- we use Typescript, ESM, minimal dependencies, functional programming
|
||||
- dont document or comment, return just the code, ready to go
|
||||
- no additional dependencies
|
||||
|
||||
## Todos
|
||||
|
||||
- [ ] function, to convert a howto (howto-model.ts) to json-ld structured data, satisfying Google, "toJSON-LD"
|
||||
Loading…
Reference in New Issue
Block a user