Some preliminary matters:
As a practical matter I find it's helpful to throw in date fields for created/modified times; it makes your life much easier when debugging data problems. Pretty much any table that's not a lookup table would benefit from it. If a table is write-once, consider foregoing the modified time column.
Be sure that you're not storing passwords in plaintext. It's beyond the scope of this question but I thought I'd take the time to mention it.
That said, I think you're prematurely worrying about things like scalability until you've actually got some data to back up your problems. That said, I do have one easy suggestion.
If a subject is of 40 lectures then it requires almost 80*40=3200 records for recording attendance of a single subject per class; so what about 6 more subjects in each semester.
It's true that you're adding a lot of records but those records so far look to be... 10 bytes each? I'm not going to look up the sizes of the data types but you're not breaking the bank with 10 byte rows. I suspect that while the users always want to have attendance available, it's likely that agreeing on retention policy can help you to cap the growth and allow you to prune attendance records from years past.
Still, you've got room to optimize. Rather than recording attendance for each
Student in a one-to-one relationship, I suggest you record attendance by picking the smaller of "attended" or "missed" and record only that. For example, if you assume most students go to lecture you would pick "missed," which means the
Attendance table will only have rows for
Students who missed lecture. If a
Student has a row in the
Attendance table he missed lecture, otherwise he was present. Of course in such a case,
Attendance should be renamed to something like
Absence or something.
Spend more time considering what you might do when your stakeholders want to add more data to your schema. Examples include adding categories or groupings, aliases or secondary names or secondary emails.
Regarding dynamic table generation
Which tables should be kept as they are [i.e should be created only once] and which tables should be created dynamically and at what interval? [i.e year-wise/ semester-wise/ any other suggestions]
This is a premature concern as you don't know what your performance problems are yet. I'd be inclined to look for badly performing tables after release and building up an archiving process depending on your needs.
I don't recommend dynamically creating tables as you'd have to make edits to the database schema on the live host that impact your application, which have the potential to bring down the application if you commit a typo. To answer your question, I recommend archiving data at the row level.
If you need old data to be highly available but infrequently accessed, you could surface it using a separate module of the application, with its own separate database. This database could be populated by an external process that moves rows (during a maintenance window) from the live/active module to the archive module.
If you don't need it to be as highly available you could export rows from the live database (during a maintenance window) to a compressed archive that is properly backed up and can be reviewed on-demand using developer tools.
The benefit with this approach is that your row operations won't take down the site; worst case is that some data is missing for awhile or performance is degraded.
As the lead developer it's up to you to determine what the needs are and act accordingly. Again, I recommend you put this kind of work off until you have a concrete problem.