Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SRS Learning Page #59

Open
hockyy opened this issue Jun 18, 2024 · 1 comment
Open

SRS Learning Page #59

hockyy opened this issue Jun 18, 2024 · 1 comment

Comments

@hockyy
Copy link
Owner

hockyy commented Jun 18, 2024

Radical support and personalization

  • Add radical manual naming entry
  • Add better radical diagram
  • Add custom user notes

SRS

Focus more on kanji / hanzi

  • Each entry can be reduced into one or multiple kanji
  • pick them up

There are several ways to put up a context:

  • a sentence;
  • or a part of entry that hanzi is part of.

How to mine

  • You can't learn a hanzi without watching, so basically you need to watch or mine in order to get a new one
  • now each hanzi have its own progress level, level is from 0 until 10

Starting a new session:

  • From the session page: A hanzi database will be updated from the entries, basically scanning for a new one from the entry, and inserting new one if not previously exist
'char' : {
   'timeCreated':  1231231,
   'level': {
       'reading': 0,
       'meaning' 0,
       'writing': 0,
   },
   'timeUpdated': {
       'reading': 123123,
       'meaning': 123123,
       'writing': 131231,
   },
}

SRS consists of

  • reading (given chinese character, give one of the readings)
  • English meaning to character (Multi choice with radical messing)
  • Character meaning to english (multi choice or open ended text)
  • Writing

Writing has different modes, the behavior is:

  • help and guide from the beginning to the end
  • plain no help (if wrong for 3 times than a shadow will show up for the entire character)
  • all mistakes will be recorded
  • Can show on top, radical help
  • Writing help can be the chinese reading and meaning

Support back

  • Entry page / meaning box now has progress for each kanji component
@hockyy
Copy link
Owner Author

hockyy commented Jun 28, 2024

import { app, ipcMain } from 'electron';
import { Level } from 'level';
import path from 'path';
const SkillConstants {
  'Writing' : 'writing'
  'Conveyance' : 'conveyance'
  'Translation' : 'translation'
}
class Skill {
  skillName: string
  // epoch
  lastUpdated: number;
  // epoch
  level: number;
  constructor (name) {
    this.skillName = name;
    this.lastUpdated = now();
    this.level = 0;
  }
}

class SRSData {
  character: string;
  // epoch
  lastUpdated: number;
  // epoch
  lastCreated: number;
  skills: map<string, Skill>
  constructor (char) {
    this.character = char;
    this.lastUpdated = now();
    this.lastCreated = now();
    for(const auto key of : SkillConstants.keys()) {
      this.skills.insert(SkillConstants[key], Skill());
    }
    this.writing = 
  }
}

class OrderedTree {
  
  // tuple is basically like c++ tuple, comparator is tie(el[0], el[1], el[2], ..) < tie(other[0], other[1], other[2], ...)
  container: Set
  generateKey: SRSData -> tuple
  constructor(SRSData[] initialData : SRSData[], keyGenerator : (SRSData a) -> tuple) {
    this.generateKey = keyGenerator
    for (const auto curData : initialData) {
      insert(curData)
    }
  }
  insert(SRSData a) {
    currentKey = this.generateKey(curData);
    // Check if key exist, if not then skip
    container.insert(currentKey, a.character)
  }
  erase(SRSData a) {
    currentKey = this.generateKey(curData);
    container.erase(currentKey)
  }
  orderOfKey(SRSData a) {
    currentKey = this.generateKey(curData);
    container.getOrder(currentKey);
    return -1 if not exist
  }
  findByOrder(int index) {
    return null if >= container.size()
    else return container[index];
  }
}

class SRSDatabase {
  static learningTrees: map<string, OrderedTree>
  static srsData: map<string, SRSData>
  static setup() {
    for(const auto key of : SkillConstants.keys()) {
      // classicKeyGen is just refresh by lastUpdated (not yet srs), srs shall compute the perfect srs formula according to the level
      this.learningTrees.add(SkillConstants[key], OrderedTree(srsData.values(), classicKeyGen));
    }

  }

  static loadSRS() {
    const prefix = `srs/${lang}/`; // Define the prefix for keys
        const learningState = {};

        // Define the range for the query
    const queryOptions = {
      gte: prefix, // Start of the range: include the prefix
      lte: `${prefix}\uFFFF`, // End of the range: highest value that still matches the prefix
    };

        // Efficiently iterate over keys within the specified range
    for await (const [key, value] of this.db.iterator(queryOptions)) {
      const strippedKey = key.substring(prefix.length);
      let parsedValue;
      parsedValue = JSON.parse(value) as SRSData
      srsData[strippedKey] = parsedValue;
    }
  }

  static insertNew(character) {
    // now let's just support 1 character srs, 1 terms is a little bit too muhcowkakoawokaw
    SRSData newSrs(character)
  }

  static updateLearningTrees(character a, skills: Skill, newLevel: number) {
    for(const auto key of : SkillConstants.keys()) {
      // classicKeyGen is just refresh by lastUpdated (not yet srs), srs shall compute the perfect srs formula according to the level
      const ptrToSRSData = this.srsData[a];
      oldKey = this.learningTrees[SkillConstants[key]].generateKey(a);
      this.learningTrees[SkillConstants[key]].erase(a);
      ptrToSRSData.skill[SkillName].level = newLevel;
      ptrToSRSData.skill[SkillName].lastUpdated = now();
      ptrToSRSData.lastUpdated = now();
      this.learningTrees[SkillConstants[key]].insert(ptrToSRSData);
    }
  }
}

class Learning {
  static dbPath;
  static db;

  static setup() {
    // Define the path to the database
    this.dbPath = path.join(app.getPath('userData'), 'learningStateDB');
    this.db = new Level(this.dbPath);

  }

  static registerHandler() {
    ipcMain.handle('loadLearningState', async (event, lang) => {
      try {
        const prefix = `${lang}/`; // Define the prefix for keys
        const learningState = {};

        // Define the range for the query
        const queryOptions = {
          gte: prefix, // Start of the range: include the prefix
          lte: `${prefix}\uFFFF`, // End of the range: highest value that still matches the prefix
        };

        // Efficiently iterate over keys within the specified range
        for await (const [key, value] of this.db.iterator(queryOptions)) {
          const strippedKey = key.substring(prefix.length);
          let parsedValue;

          try {
            parsedValue = JSON.parse(value);
            if (typeof parsedValue.level !== 'number' || typeof parsedValue.updTime !== 'number') {
              throw new Error('Invalid format');
            }
          } catch (e) {
            // If parsing fails, assume it's an old format (number)
            parsedValue = {
              level: parseInt(value, 10),
              updTime: Date.now()
            };
            await this.db.put(key, JSON.stringify(parsedValue));
          }

          learningState[strippedKey] = parsedValue;
        }

        return learningState;
      } catch (error) {
        console.error('Error loading learning state:', error);
        return {};
      }
    });


    // Handler to update a specific content's learning state
    ipcMain.handle('updateContent', async (event, content, lang, data) => {
      if (!content) return true;
      try {
        await this.db.put(`${lang}/${content}`, JSON.stringify(data));
        return true; // Indicate success
      } catch (error) {
        console.error('Error updating content:', error);
        return false; // Indicate failure
      }
    });

    // Handler to update a batch of contents' learning states
    ipcMain.handle('updateContentBatch', async (event, contents: LearningStateType, lang) => {
      if (!contents) return true;
      try {
        for (const [key, value] of Object.entries(contents)) {
          const goUpdate = async () => {
            await this.db.put(`${lang}/${key}`, JSON.stringify(value));
          }
          this.db.get(`${lang}/${key}`).then(async val => {
            const parsedVal = JSON.parse(val);
            if (parsedVal.level < value.level) await goUpdate();
          }).catch(async () => {
            await goUpdate();
          })
        }
        return true; // Indicate success
      } catch (error) {
        console.error('Error updating content:', error);
        return false; // Indicate failure
      }
    });
  }
}

export default Learning;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant